home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-03-27 | 124.8 KB | 3,077 lines | [TEXT/ROSA] |
- Common Lisp the Language, 2nd Edition
- -------------------------------------------------------------------------------
-
- 29. Conditions
-
- By Kent M. Pitman
-
- [change_begin]
- PREFACE: The language defined by the first edition contained an enormous
- lacuna: although facilities were specified for signaling errors, no means was
- defined for handling errors. This occurred not through neglect of the issue,
- but because this part of the Lisp language generally was in a state of flux.
- There were several proposals at the time. The committee, finding that it could
- not agree on any one proposal, agreed to disagree and omit error handling from
- Common Lisp for the time being. This defect has now been addressed.
-
- X3J13 voted in June 1988 (CONDITION-SYSTEM) to adopt the Common Lisp
- Condition System as a part of the forthcoming draft Common Lisp standard. X3J13
- voted in March 1989 (ZLOS-CONDITIONS) to amend the specification of
- conditions to integrate them with the Common Lisp Object System (see chapter
- 28). X3J13 voted in June 1989 (CONDITION-RESTARTS) to amend the specification
- of restarts in certain ways. These amendments have been incorporated here with
- little further comment.
-
- This chapter presents the bulk of the Common Lisp Condition System proposal,
- written by Kent M. Pitman and amended by X3J13. I have edited it only very
- lightly to conform to the overall style of this book and have inserted a small
- number of bracketed remarks identified by the initials GLS. Please see the
- Acknowledgments to this second edition for the author's acknowledgments to
- others who contributed to the Condition System proposal.
-
- -Guy L. Steele Jr.
-
- [change_end]
- -------------------------------------------------------------------------------
-
- * Introduction
- * Changes in Terminology
- * Survey of Concepts
- o Signaling Errors
- o Trapping Errors
- o Handling Conditions
- o Object-Oriented Basis of Condition Handling
- o Restarts
- o Anonymous Restarts
- o Named Restarts
- o Restart Functions
- o Comparison of Restarts and Catch/Throw
- o Generalized Restarts
- o Interactive Condition Handling
- o Serious Conditions
- o Non-Serious Conditions
- o Condition Types
- o Signaling Conditions
- o Resignaling Conditions
- o Condition Handlers
- o Printing Conditions
- * Program Interface to the Condition System
- o Signaling Conditions
- o Assertions
- o Exhaustive Case Analysis
- o Handling Conditions
- o Defining Conditions
- o Creating Conditions
- o Establishing Restarts
- o Finding and Manipulating Restarts
- o Warnings
- o Restart Functions
- o Debugging Utilities
- * Predefined Condition Types
-
- -------------------------------------------------------------------------------
-
- 29.1. Introduction
-
- [change_begin]
- Often we find it useful to describe a function in terms of its behavior in
- ``normal situations.'' For example, we may say informally that the function +
- returns the sum of its arguments or that the function read-char returns the
- next available character on a given input stream.
-
- Sometimes, however, an ``exceptional situation'' will arise that does not fit
- neatly into such descriptions. For example, + might receive an argument that is
- not a number, or read-char might receive as a single argument a stream that has
- no more available characters. This distinction between normal and exceptional
- situations is in some sense arbitrary but is often very useful in practice.
-
- For example, suppose a function f were defined to allow only integer arguments
- but also guaranteed to detect and signal an error for non-integer arguments.
- Such a description is in fact internally inconsistent (that is, paradoxical)
- because the function's behavior is well-defined for non-integers. Yet we would
- not want this annoying paradox to force description of f as a function that
- accepts any kind of argument (just in case f is being called only as a quick
- way to signal an error, for example). Using the normal/exceptional distinction,
- we can say clearly that f accepts integers in the normal situation and signals
- an error in exceptional situations. Moreover, we can say that when we refer to
- the definition of a function informally, it is acceptable to speak only of its
- normal behavior. For example, we can speak informally about f as a function
- that accepts only integers without feeling that we are committing some awful
- fraud.
-
- Not all exceptional situations are errors. For example, a program that is
- directing the typing of a long line of text may come to an end-of-line. It is
- possible that no real harm will result from failing to signal end-of-line to
- its caller because the operating system will simply force a carriage return on
- the output device, which will continue typing on the next line. However, it may
- still be interesting to establish a protocol whereby the printing program can
- inform its caller of end-of-line exceptions. The caller could then opt to deal
- with these situations in interesting ways at certain times. For example, a
- caller might choose to terminate printing, obtaining an end-of-line truncation.
- The important thing, however, is that the failure of the caller to provide
- advice about the situation need not prevent the printer program from operating
- correctly.
-
- Mechanisms for dealing with exceptional situations vary widely. When an
- exceptional situation is encountered, a program may attempt to handle it by
- returning a distinguished value, returning an additional value, setting a
- variable, calling a function, performing a special transfer of control, or
- stopping the program altogether and entering the debugger.
-
- For the most part, the facilities described in this chapter do not introduce
- any fundamentally new way of dealing with exceptional situations. Rather, they
- encapsulate and formalize useful patterns of data and control flow that have
- been seen to be useful in dealing with exceptional situations.
-
- A proper conceptual approach to errors should perhaps begin from first
- principles, with a discussion of conditions in general, and eventually work up
- to the concept of an error as just one of the many kinds of conditions.
- However, given the primitive state of error-handling technology, a proper
- buildup may be as inappropriate as requiring that a beggar learn to cook a
- gourmet meal before being allowed to eat. Thus, we deal first with the
- essentials-error handling-and then go back later to fill in the missing
- details.
- [change_end]
-
- -------------------------------------------------------------------------------
- 29.2. Changes in Terminology
-
- [change_begin]
- In this section, we introduce changes to the terminology defined in section
- 1.2.4.
-
- A condition is an interesting situation in a program that has been detected and
- announced. Later we allow this term also to refer to objects that programs use
- to represent such situations.
-
- An error is a condition in which normal program execution may not continue
- without some form of intervention (either interactively by the user or under
- some sort of program control, as described below).
-
- The process by which a condition is formally announced by a program is called
- signaling. The function signal is the primitive mechanism by which such
- announcement is done. Other abstractions, such as error and cerror, are built
- using signal.
-
- The first edition is ambiguous about the reason why a particular program action
- ``is an error.'' There are two principal reasons why an action may be an error
- without being required to signal an error:
-
- * Detecting the error might be prohibitively expensive.
-
- For example, (+ nil 3) is an error. It is likely that the designers of
- Common Lisp believed this would be an error in all implementations but
- felt it might be excessively expensive to detect the problem in compiled
- code on stock hardware, so they did not require that it signal an error.
-
- * Some implementations might implement the behavior as an extension.
-
- For example, (loop for x from 1 to 3 do (print x)) is an error because
- loop is not defined to take atoms in its body. In fact, however, some
- implementations offer an extension that makes this well-defined. In order
- to leave room for such extensions, the first edition used the ``is an
- error'' terminology to keep implementors from being forced to signal an
- error in the extended implementations.
-
- [This example was written well before the vote by X3J13 in January 1989 to
- add exactly this extension to the forthcoming draft standard (see chapter
- 26).-GLS]
-
- In this chapter, we use the following terminology. [Compare this to the
- terminology presented in section 28.1.1.-GLS]
-
- * If the signaling of a condition or error is part of a function's contract
- in all situations, we say that it ``signals'' or ``must signal'' that
- condition or error.
-
- * If the signaling of a condition or error is optional for some important
- reason (such as performance), we say that the program ``might signal''
- that condition or error. In this case, we are defining the operation to be
- illegal in all implementations, but allowing some implementations to fail
- to detect the error.
-
- * If an action is left undefined for the sake of implementation-dependent
- extension, we say that it ``is undefined'' or ``has undefined effect.''
- This means that it is not possible to depend portably upon the effects of
- that action. A program that has undefined effect may enter the debugger,
- transfer control, or modify data in unpredictable ways.
-
- * In the special case where only the return value of an operation is not
- well defined but any side effect and transfer-of-control behavior is well
- defined, we say that it has ``undefined value.'' In this case, the number
- and nature of the return values is not defined, but the function can
- reasonably be expected to return. It is worth noting that under this
- description, there are some (though not many) legitimate ways in which
- such return value(s) can be used. For example, if the function foo has no
- side effects and undefined value, the expression (length (list (foo))) is
- completely well defined even for portable code. However, the effect of
- (print (list (foo))) is not well defined.
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3. Survey of Concepts
-
- [change_begin]
- This section discusses various aspects of the condition system by topic,
- illustrating them with extensive examples. The next section contains
- definitions of specific functions, macros, and other facilities.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- * Signaling Errors
- * Trapping Errors
- * Handling Conditions
- * Object-Oriented Basis of Condition Handling
- * Restarts
- * Anonymous Restarts
- * Named Restarts
- * Restart Functions
- * Comparison of Restarts and Catch/Throw
- * Generalized Restarts
- * Interactive Condition Handling
- * Serious Conditions
- * Non-Serious Conditions
- * Condition Types
- * Signaling Conditions
- * Resignaling Conditions
- * Condition Handlers
- * Printing Conditions
-
- -------------------------------------------------------------------------------
-
- 29.3.1. Signaling Errors
-
- [change_begin]
- Conceptually, signaling an error in a program is an admission by that program
- that it does not know how to continue and requires external intervention. Once
- an error is signaled, any decision about how to continue must come from the
- ``outside.''
-
- The simplest way to signal an error is to use the error function with
- format-style arguments describing the error for the sake of the user interface.
- If error is called and there are no active handlers (described in sections
- 29.3.2 and 29.3.3), the debugger will be entered and the error message will be
- typed out. For example:
-
- Lisp> (defun factorial (x)
- (cond ((or (not (typep x 'integer)) (minusp x))
- (error "~S is not a valid argument to FACTORIAL."
- x))
- ((zerop x) 1)
- (t (* x (factorial (- x 1))))))
- => FACTORIAL
- Lisp> (factorial 20)
- => 2432902008176640000
- Lisp> (factorial -1)
- Error: -1 is not a valid argument to FACTORIAL.
- To continue, type :CONTINUE followed by an option number:
- 1: Return to Lisp Toplevel.
- Debug>
-
- In general, a call to error cannot directly return. Unless special work has
- been done to override this behavior, the debugger will be entered and there
- will be no option to simply continue.
-
- The only exception may be that some implementations may provide debugger
- commands for interactively returning from individual stack frames; even then,
- however, such commands should never be used except by someone who has read the
- erring code and understands the consequences of continuing from that point. In
- particular, the programmer should feel confident about writing code like this:
-
- (defun wargames:no-win-scenario ()
- (when (true) (error "Pushing the button would be stupid."))
- (push-the-button))
-
- In this scenario, there should be no chance that the function error will return
- and the button will be pushed.
-
- -------------------------------------------------------------------------------
- Remark: It should be noted that the notion of ``no chance'' that the button
- will be pushed is relative only to the language model; it assumes that the
- language is accurately implemented. In practice, compilers have bugs, computers
- have glitches, and users have been known to interrupt at inopportune moments
- and use the debugger to return from arbitrary stack frames. Such violations of
- the language model are beyond the scope of the condition system but not
- necessarily beyond the scope of potential failures that the programmer should
- consider and defend against. The possibility of such unusual failures may of
- course also influence the design of code meant to handle less drastic
- situations, such as maintaining a database uncorrupted.-KMP and GLS
- -------------------------------------------------------------------------------
-
- In some cases, the programmer may have a single, well-defined idea of a
- reasonable recovery strategy for this particular error. In that case, he can
- use the function cerror, which specifies information about what would happen if
- the user did simply continue from the call to cerror. For example:
-
- Lisp> (defun factorial (x)
- (cond ((not (typep x 'integer))
- (error "~S is not a valid argument to FACTORIAL."
- x))
- ((minusp x)
- (let ((x-magnitude (- x)))
- (cerror "Compute -(~D!) instead."
- "(-~D)! is not defined." x-magnitude)
- (- (factorial x-magnitude))))
- ((zerop x) 1)
- (t (* x (factorial (- x 1))))))
- => FACTORIAL
- Lisp> (factorial -3)
- Error: (-3)! is not defined.
- To continue, type :CONTINUE followed by an option number:
- 1: Compute -(3!) instead.
- 2: Return to Lisp Toplevel.
- Debug> :continue 1
- => -6
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.1. Signaling Errors
-
- [change_begin]
- Conceptually, signaling an error in a program is an admission by that program
- that it does not know how to continue and requires external intervention. Once
- an error is signaled, any decision about how to continue must come from the
- ``outside.''
-
- The simplest way to signal an error is to use the error function with
- format-style arguments describing the error for the sake of the user interface.
- If error is called and there are no active handlers (described in sections
- 29.3.2 and 29.3.3), the debugger will be entered and the error message will be
- typed out. For example:
-
- Lisp> (defun factorial (x)
- (cond ((or (not (typep x 'integer)) (minusp x))
- (error "~S is not a valid argument to FACTORIAL."
- x))
- ((zerop x) 1)
- (t (* x (factorial (- x 1))))))
- => FACTORIAL
- Lisp> (factorial 20)
- => 2432902008176640000
- Lisp> (factorial -1)
- Error: -1 is not a valid argument to FACTORIAL.
- To continue, type :CONTINUE followed by an option number:
- 1: Return to Lisp Toplevel.
- Debug>
-
- In general, a call to error cannot directly return. Unless special work has
- been done to override this behavior, the debugger will be entered and there
- will be no option to simply continue.
-
- The only exception may be that some implementations may provide debugger
- commands for interactively returning from individual stack frames; even then,
- however, such commands should never be used except by someone who has read the
- erring code and understands the consequences of continuing from that point. In
- particular, the programmer should feel confident about writing code like this:
-
- (defun wargames:no-win-scenario ()
- (when (true) (error "Pushing the button would be stupid."))
- (push-the-button))
-
- In this scenario, there should be no chance that the function error will return
- and the button will be pushed.
-
- -------------------------------------------------------------------------------
- Remark: It should be noted that the notion of ``no chance'' that the button
- will be pushed is relative only to the language model; it assumes that the
- language is accurately implemented. In practice, compilers have bugs, computers
- have glitches, and users have been known to interrupt at inopportune moments
- and use the debugger to return from arbitrary stack frames. Such violations of
- the language model are beyond the scope of the condition system but not
- necessarily beyond the scope of potential failures that the programmer should
- consider and defend against. The possibility of such unusual failures may of
- course also influence the design of code meant to handle less drastic
- situations, such as maintaining a database uncorrupted.-KMP and GLS
- -------------------------------------------------------------------------------
-
- In some cases, the programmer may have a single, well-defined idea of a
- reasonable recovery strategy for this particular error. In that case, he can
- use the function cerror, which specifies information about what would happen if
- the user did simply continue from the call to cerror. For example:
-
- Lisp> (defun factorial (x)
- (cond ((not (typep x 'integer))
- (error "~S is not a valid argument to FACTORIAL."
- x))
- ((minusp x)
- (let ((x-magnitude (- x)))
- (cerror "Compute -(~D!) instead."
- "(-~D)! is not defined." x-magnitude)
- (- (factorial x-magnitude))))
- ((zerop x) 1)
- (t (* x (factorial (- x 1))))))
- => FACTORIAL
- Lisp> (factorial -3)
- Error: (-3)! is not defined.
- To continue, type :CONTINUE followed by an option number:
- 1: Compute -(3!) instead.
- 2: Return to Lisp Toplevel.
- Debug> :continue 1
- => -6
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.2. Trapping Errors
-
- [change_begin]
- By default, a call to error will force entry into the debugger. You can
- override that behavior in a variety of ways. The simplest (and most blunt) tool
- for inhibiting entry to the debugger on an error is to use ignore-errors. In
- the normal situation, forms in the body of ignore-errors are evaluated
- sequentially and the last value is returned. If a condition of type error is
- signaled, ignore-errors immediately returns two values, namely nil and the
- condition that was signaled; the debugger is not entered and no error message
- is printed. For example:
-
- Lisp> (setq filename "nosuchfile")
- => "nosuchfile"
- Lisp> (ignore-errors (open filename :direction :input))
- => NIL and #<FILE-ERROR 3437523>
-
- The second return value is an object that represents the kind of error. This is
- explained in greater detail in section 29.3.4.
-
- In many cases, however, ignore-errors is not desirable because it deals with
- too many kinds of errors. Contrary to the belief of some, a program that does
- not enter the debugger is not necessarily better than one that does. Excessive
- use of ignore-errors may keep the program out of the debugger, but it may not
- increase the program's reliability, because the program may continue to run
- after encountering errors other than those you meant to work past. In general,
- it is better to attempt to deal only with the particular kinds of errors that
- you believe could legitimately happen. That way, if an unexpected error comes
- along, you will still find out about it.
-
- ignore-errors is a useful special case built from a more general facility,
- handler-case, that allows the programmer to deal with particular kinds of
- conditions (including non-error conditions) without affecting what happens when
- other kinds of conditions are signaled. For example, an effect equivalent to
- that of ignore-errors above is achieved in the following example:
-
- Lisp> (setq filename "nosuchfile")
- => "nosuchfile"
- Lisp> (handler-case (open filename :direction :input)
- (error (condition)
- (values nil condition)))
- => NIL and #<FILE-ERROR 3437525>
-
- However, using handler-case, one can indicate a more specific condition type
- than just ``error.'' Condition types are explained in detail later, but the
- syntax looks roughly like the following:
-
- Lisp> (makunbound 'filename)
- => FILENAME
- Lisp> (handler-case (open filename :direction :input)
- (file-error (condition)
- (values nil condition)))
- Error: The variable FILENAME is unbound.
- To continue, type :CONTINUE followed by an option number:
- 1: Retry getting the value of FILENAME.
- 2: Specify a value of FILENAME to use this time.
- 3: Specify a value of FILENAME to store and use.
- 4: Return to Lisp Toplevel.
- Debug>
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.3. Handling Conditions
-
- [change_begin]
- Blind transfer of control to a handler-case is only one possible kind of
- recovery action that can be taken when a condition is signaled. The low-level
- mechanism offers great flexibility in how to continue once a condition has been
- signaled.
-
- The basic idea behind condition handling is that a piece of code called the
- signaler recognizes and announces the existence of an exceptional situation
- using signal or some function built on signal (such as error).
-
- The process of signaling involves the search for and invocation of a handler, a
- piece of code that will attempt to deal appropriately with the situation.
-
- If a handler is found, it may either handle the situation, by performing some
- non-local transfer of control, or decline to handle it, by failing to perform a
- non-local transfer of control. If it declines, other handlers are sought.
-
- Since the lexical environment of the signaler might not be available to
- handlers, a data structure called a condition is created to represent
- explicitly the relevant state of the situation. A condition either is created
- explicitly using make-condition and then passed to a function such as signal,
- or is created implicitly by a function such as signal when given appropriate
- non-condition arguments.
-
- In order to handle the error, a handler is permitted to use any non-local
- transfer of control such as go to a tag in a tagbody, return from a block, or
- throw to a catch. In addition, structured abstractions of these primitives are
- provided for convenience in exception handling.
-
- A handler can be made dynamically accessible to a program by use of
- handler-bind. For example, to create a handler for a condition of type
- arithmetic-error, one might write:
-
- (handler-bind ((arithmetic-error handler))body)
-
- The handler is a function of one argument, the condition. If a condition of the
- designated type is signaled while the body is executing (and there are no
- intervening handlers), the handler would be invoked on the given condition,
- allowing it the option of transferring control. For example, one might write a
- macro that executes a body, returning either its value(s) or the two values nil
- and the condition:
-
- (defmacro without-arithmetic-errors (&body forms)
- (let ((tag (gensym)))
- `(block ,tag
- (handler-bind ((arithmetic-error
- #'(lambda (c) ;Argument c is a condition
- (return-from ,tag (values nil c)))))
- ,@body))))
-
- The handler is executed in the dynamic context of the signaler, except that the
- set of available condition handlers will have been rebound to the value that
- was active at the time the condition handler was made active. If a handler
- declines (that is, it does not transfer control), other handlers are sought. If
- no handler is found and the condition was signaled by error or cerror (or some
- function such as assert that behaves like these functions), the debugger is
- entered, still in the dynamic context of the signaler.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.4. Object-Oriented Basis of Condition Handling
-
- [change_begin]
- Of course, the ability of the handler to usefully handle an exceptional
- situation is related to the quality of the information it is provided. For
- example, if all errors were signaled by
-
- (error "some format string")
-
- then the only piece of information that would be accessible to the handler
- would be an object of type simple-error that had a slot containing the format
- string.
-
- If this were done, string-equal would be the preferred way to tell one error
- from another, and it would be very hard to allow flexibility in the
- presentation of error messages because existing handlers would tend to be
- broken by even tiny variations in the wording of an error message. This
- phenomenon has been the major failing of most error systems previously
- available in Lisp. It is fundamentally important to decouple the error message
- string (the human interface) from the objects that formally represent the error
- state (the program interface). We therefore have the notion of typed
- conditions, and of formal operations on those conditions that make them
- inspectable in a structured way.
-
- This object-oriented approach to condition handling has the following important
- advantages over a text-based approach:
-
- * Conditions are classified according to subtype relationships, making it
- easy to test for categories of conditions.
-
- * Conditions have named slot values through which parameters are conveyed
- from the program that signals the condition to the program that handles
- it.
-
- * Inheritance of methods and slots reduces the amount of explicit
- specification necessary to achieve various interesting effects.
-
- Some condition types are defined by this document, but the set of condition
- types is extensible using define-condition. Common Lisp condition types are in
- fact CLOS classes, and condition objects are ordinary CLOS objects;
- define-condition merely provides an abstract interface that is a bit more
- convenient than defclass for defining conditions.
-
- Here, as an example, we define a two-argument function called divide that is
- patterned after the / function but does some stylized error checking:
-
- (defun divide (numerator denominator)
- (cond ((or (not (numberp numerator))
- (not (numberp denominator)))
- (error "(DIVIDE '~S '~S) - Bad arguments."
- numerator denominator))
- ((zerop denominator)
- (error 'division-by-zero
- :operator 'divide
- :operands (list numerator denominator)))
- (t ...)))
-
- Note that in the first clause we have used error with a string argument and in
- the second clause we have named a particular condition type, division-by-zero.
- In the case of a string argument, the condition type that will be signaled is
- simple-error.
-
- The particular kind of error that is signaled may be important in cases where
- handlers are active. For example, simple-error inherits from type error, which
- in turn inherits from type condition. On the other hand, division-by-zero
- inherits from arithmetic-error, which inherits from error, which inherits from
- condition. So if a handler existed for arithmetic-error while a
- division-by-zero condition was signaled, that handler would be tried; however,
- if a simple-error condition were signaled in the same context, the handler for
- type arithmetic-error would not be tried.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.5. Restarts
-
- [change_begin]
- In older Lisp dialects (such as MacLisp), an attempt to signal an error of a
- given type often carried with it an implicit promise to support the standard
- recovery strategy for that type of error. If the signaler knew the type of
- error but for whatever reason was unable to deal with the standard recovery
- strategy for that kind of error, it was necessary to signal an untyped error
- (for which there was no defined recovery strategy). This sometimes led to
- confusion when people signaled typed errors without realizing the full
- implications of having done so, but more often than not it meant that users
- simply avoided typed errors altogether.
-
- The Common Lisp Condition System, which is modeled after the Zetalisp condition
- system, corrects this troublesome aspect of previous Lisp dialects by creating
- a clear separation between the act of signaling an error of a particular type
- and the act of saying that a particular way of recovery is appropriate. In the
- divide example above, simply signaling an error does not imply a willingness on
- the part of the signaler to cooperate in any corrective action. For example,
- the following sample interaction illustrates that the only recovery action
- offered for this error is ``Return to Lisp Toplevel'':
-
- Lisp> (+ (divide 3 0) 7)
- Error: Attempt to divide 3 by 0.
- To continue, type :CONTINUE followed by an option number:
- 1: Return to Lisp Toplevel.
- Debug> :continue 1
- Returned to Lisp Toplevel.
- Lisp>
-
- When an error is detected and the function error is called, execution cannot
- continue normally because error will not directly return. Control can be
- transferred to other points in the program, however, by means of specially
- established ``restarts.''
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.6. Anonymous Restarts
-
- [change_begin]
- The simplest kind of restart involves structured transfer of control using a
- macro called restart-case. The restart-case form allows execution of a piece of
- code in a context where zero or more restarts are active, and where if one of
- those restarts is ``invoked,'' control will be transferred to the corresponding
- clause in the restart-case form. For example, we could rewrite the previous
- divide example as follows.
-
- (defun divide (numerator denominator)
- (loop
- (restart-case
- (return
- (cond ((or (not (numberp numerator))
- (not (numberp denominator)))
- (error "(DIVIDE '~S '~S) - Bad arguments."
- numerator denominator))
- ((zerop denominator)
- (error 'division-by-zero
- :operator 'divide
- :operands (list numerator denominator)))
- (t ...)))
- (nil (arg1 arg2)
- :report "Provide new arguments for use by DIVIDE."
- :interactive
- (lambda ()
- (list (prompt-for 'number "Numerator: ")
- (prompt-for 'number "Denominator: ")))
- (setq numerator arg1 denominator arg2))
- (nil (result)
- :report "Provide a value to return from DIVIDE."
- :interactive
- (lambda () (list (prompt-for 'number "Result: ")))
- (return result)))))
-
- -------------------------------------------------------------------------------
- Remark: The function prompt-for used in this chapter in a number of places is
- not a part of Common Lisp. It is used in the examples in this chapter only to
- keep the presentation simple. It is assumed to accept a type specifier and
- optionally a format string and associated arguments. It uses the format string
- and associated arguments as part of an interactive prompt, and uses read to
- read a Lisp object; however, only an object of the type indicated by the type
- specifier is accepted.
-
- The question of whether or not prompt-for (or something like it) would be a
- useful addition to Common Lisp is under consideration by X3J13, but as of
- January 1989 no action has been taken. In spite of its use in a number of
- examples, nothing in the Common Lisp Condition System depends on this function.
- -------------------------------------------------------------------------------
-
- In the example, the nil at the head of each clause means that it is an
- ``anonymous'' restart. Anonymous restarts are typically invoked only from
- within the debugger. As we shall see later, it is possible to have ``named
- restarts'' that may be invoked from code without the need for user
- intervention.
-
- If the arguments to anonymous restarts are not optional, then special
- information must be provided about what the debugger should use as arguments.
- Here the :interactive keyword is used to specify that information.
-
- The :report keyword introduces information to be used when presenting the
- restart option to the user (by the debugger, for example).
-
- Here is a sample interaction that takes advantage of the restarts provided by
- the revised definition of divide:
-
- Lisp> (+ (divide 3 0) 7)
- Error: Attempt to divide 3 by 0.
- To continue, type :CONTINUE followed by an option number:
- 1: Provide new arguments for use by the DIVIDE function.
- 2: Provide a value to return from the DIVIDE function.
- 3: Return to Lisp Toplevel.
- Debug> :continue 1
-
- Numerator: 4
- Denominator: 2
- => 9
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.7. Named Restarts
-
- [change_begin]
- In addition to anonymous restarts, one can have named restarts, which can be
- invoked by name from within code. As a trivial example, one could write
-
- (restart-case (invoke-restart 'foo 3)
- (foo (x) (+ x 1)))
-
- to add 3 to 1, returning 4. This trivial example is conceptually analogous to
- writing:
-
- (+ (catch 'something (throw 'something 3)) 1)
-
- For a more realistic example, the code for the function symbol-value might
- signal an unbound variable error as follows:
-
- (restart-case (error "The variable ~S is unbound." variable)
- (continue ()
- :report
- (lambda (s) ;Argument s is a stream
- (format s "Retry getting the value of ~S." variable))
- (symbol-value variable))
- (use-value (value)
- :report
- (lambda (s) ;Argument s is a stream
- (format s "Specify a value of ~S to use this time."
- variable))
- value)
- (store-value (value)
- :report
- (lambda (s) ;Argument s is a stream
- (format s "Specify a value of ~S to store and use."
- variable))
- (setf (symbol-value variable) value)
- value))
-
- If this were part of the implementation of symbol-value, then it would be
- possible for users to write a variety of automatic handlers for unbound
- variable errors. For example, to make unbound variables evaluate to themselves,
- one might write
-
- (handler-bind ((unbound-variable
- #'(lambda (c) ;Argument c is a condition
- (when (find-restart 'use-value)
- (invoke-restart 'use-value
- (cell-error-name c))))))
- body)
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.8. Restart Functions
-
- [change_begin]
- For commonly used restarts, it is conventional to define a program interface
- that hides the use of invoke-restart. Such program interfaces to restarts are
- called restart functions.
-
- The normal convention is for the function to share the name of the restart. The
- pre-defined functions abort, continue, muffle-warning, store-value, and
- use-value are restart functions. With use-value the above example of
- handler-bind could have been written more concisely as
-
- (handler-bind ((unbound-variable
- #'(lambda (c) ;Argument c is a condition
- (use-value (cell-error-name c)))))
- body)
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.9. Comparison of Restarts and Catch/Throw
-
- [change_begin]
- One important feature that restart-case (or restart-bind) offers that catch
- does not is the ability to reason about the available points to which control
- might be transferred without actually attempting the transfer. One could, for
- example, write
-
- (ignore-errors (throw ...))
-
- which is a sort of poor man's variation of
-
- (when (find-restart 'something)
- (invoke-restart 'something))
-
- but there is no way to use ignore-errors and throw to simulate something like
-
- (when (and (find-restart 'something)
- (find-restart 'something-else))
- (invoke-restart 'something))
-
- or even just
-
- (when (and (find-restart 'something)
- (yes-or-no-p "Do something? "))
- (invoke-restart 'something))
-
- because the degree of inspectability that comes with simply writing
-
- (ignore-errors (throw ...))
-
- is too primitive-getting the desired information also forces transfer of
- control, perhaps at a time when it is not desirable.
-
- Many programmers have previously evolved strategies like the following on a
- case-by-case basis:
-
- (defvar *foo-tag-is-available* nil)
-
- (defun fn-1 ()
- (catch 'foo
- (let ((*foo-tag-is-available* t))
- ... (fn-2) ...)))
-
- (defun fn-2 ()
- ...
- (if *foo-tag-is-available* (throw 'foo t))
- ...)
-
- The facility provided by restart-case and find-restart is intended to provide a
- standardized protocol for this sort of information to be communicated between
- programs that were developed independently so that individual variations from
- program to program do not thwart the overall modularity and debuggability of
- programs.
-
- Another difference between the restart facility and the catch/throw facility is
- that a catch with any given tag completely shadows any outer pending catch that
- uses the same tag. Because of the presence of compute-restarts, however, it is
- possible to see shadowed restarts, which may be very useful in some situations
- (particularly in an interactive debugger).
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.10. Generalized Restarts
-
- [change_begin]
- restart-case is a mechanism that allows only imperative transfer of control for
- its associated restarts. restart-case is built on a lower-level mechanism
- called restart-bind, which does not force transfer of control.
-
- restart-bind is to restart-case as handler-bind is to handler-case. The syntax
- is
-
- (restart-bind ((name function . options)) . body)
-
- The body is executed in a dynamic context within which the function will be
- called whenever (invoke-restart 'name) is executed. The options are
- keyword-style and are used to pass information such as that provided with the
- :report keyword in restart-case.
-
- A restart-case expands into a call to restart-bind where the function simply
- does an unconditional transfer of control to a particular body of code, passing
- along ``argument'' information in a structured way.
-
- It is also possible to write restarts that do not transfer control. Such
- restarts may be useful in implementing various special commands for the
- debugger that are of interest only in certain situations. For example, one
- might imagine a situation where file space was exhausted and the following was
- done in an attempt to free space in directory dir:
-
- (restart-bind ((nil #'(lambda () (expunge-directory dir))
- :report-function
- #'(lambda (stream)
- (format stream "Expunge ~A."
- (directory-namestring dir)))))
- (cerror "Try this file operation again."
- 'directory-full :directory dir))
-
- In this case, the debugger might be entered and the user could first perform
- the expunge (which would not transfer control from the debugger context) and
- then retry the file operation:
-
- Lisp> (open "FOO" :direction :output)
- Error: The directory PS:<JDOE> is full.
- To continue, type :CONTINUE followed by an option number:
- 1: Try this file operation again.
- 2: Expunge PS:<JDOE>.
- 3: Return to Lisp Toplevel.
- Debug> :continue 2
- Expunging PS:<JDOE> ... 3 records freed.
- Debug> :continue 1
- => #<OUTPUT-STREAM "PS:<JDOE>FOO.LSP" 2323473>
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.11. Interactive Condition Handling
-
- [change_begin]
- When a program does not know how to continue, and no active handler is able to
- advise it, the ``interactive condition handler,'' or ``debugger,'' can be
- entered. This happens implicitly through the use of functions such as error and
- cerror, or explicitly through the use of the function invoke-debugger.
-
- The interactive condition handler never returns directly; it returns only
- through structured non-local transfer of control to specially defined restart
- points that can be set up either by the system or by user code. The mechanisms
- that support the establishment of such structured restart points for portable
- code are outlined in sections 29.3.5 through 29.3.10.
-
- Actually, implementations may also provide extended debugging facilities that
- allow return from arbitrary stack frames. Although such commands are frequently
- useful in practice, their effects are implementation-dependent because they
- violate the Common Lisp program abstraction. The effect of using such commands
- is undefined with respect to Common Lisp.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.12. Serious Conditions
-
- [change_begin]
- The ignore-errors macro will trap conditions of type error. There are, however,
- conditions that are not of type error.
-
- Some conditions are not considered errors but are still very serious, so we
- call them serious conditions and we use the type serious-condition to represent
- them. Conditions such as those that might be signaled for ``stack overflow'' or
- ``storage exhausted'' are in this category.
-
- The type error is a subtype of serious-condition, and it would technically be
- correct to use the term ``serious condition'' to refer to all serious
- conditions whether errors or not. However, normally we use the term ``serious
- condition'' to refer to things of type serious-condition but not of type error.
-
- The point of the distinction between errors and other serious conditions is
- that some conditions are known to occur for reasons that are beyond the scope
- of Common Lisp to specify clearly. For example, we know that a stack will
- generally be used to implement function calling, and we know that stacks tend
- to be of finite size and are prone to overflow. Since the available stack size
- may vary from implementation to implementation, from session to session, or
- from function call to function call, it would be confusing to have expressions
- such as (ignore-errors (+ a b)) return a number sometimes and nil other times
- if a and b were always bound to numbers and the stack just happened to overflow
- on a particular call. For this reason, only conditions of type error and not
- all conditions of type serious-condition are trapped by ignore-errors. To trap
- other conditions, a lower-level facility must be used (such as handler-bind or
- handler-case).
-
- By convention, the function error is preferred over signal to signal conditions
- of type serious-condition (including those of type error). It is the use of the
- function error, and not the type of the condition being signaled, that actually
- causes the debugger to be entered.
-
- -------------------------------------------------------------------------------
- Compatibility note: The Common Lisp Condition System differs from that of
- Zetalisp in this respect. In Zetalisp the debugger is entered for an unhandled
- signal if the error function is used or if the condition is of type error.
- -------------------------------------------------------------------------------
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.13. Non-Serious Conditions
-
- [change_begin]
- Some conditions are neither errors nor serious conditions. They are signaled to
- give other programs a chance to intervene, but if no action is taken,
- computation simply continues normally.
-
- For example, an implementation might choose to signal a non-serious (and
- implementation-dependent) condition called end-of-line when output reaches the
- last character position on a line of character output. In such an
- implementation, the signaling of this condition might allow a convenient way
- for other programs to intervene, producing output that is truncated at the end
- of a line.
-
- By convention, the function signal is used to signal conditions that are not
- serious. It would be possible to signal serious conditions using signal, and
- the debugger would not be entered if the condition went unhandled. However, by
- convention, handlers will generally tend to assume that serious conditions and
- errors were signaled by calling the error function (and will therefore force
- entry to the interactive condition handler) and that they should work to avoid
- this.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.14. Condition Types
-
- [change_begin]
- Some types of conditions are predefined by the system. All types of conditions
- are subtypes of condition. That is, (typep x 'condition) is true if and only if
- the value of x is a condition.
-
- Implementations supporting multiple (or non-hierarchical) type inheritance are
- expressly permitted to exploit multiple inheritance in the tree of condition
- types as implementation-dependent extensions, as long as such extensions are
- compatible with the specifications in this chapter. [X3J13 voted in March 1989
- (ZLOS-CONDITIONS) to integrate the Condition System and the Object System, so
- multiple inheritance is always available for condition types.-GLS]
-
- In order to avoid problems in portable code that runs both in systems with
- multiple type inheritance and in systems without it, programmers are explicitly
- warned that while all correct Common Lisp implementations will ensure that
- (typep c 'condition) is true for all conditions c (and all subtype
- relationships indicated in this chapter will also be true), it should not be
- assumed that two condition types specified to be subtypes of the same third
- type are disjoint. (In some cases, disjoint subtypes are identified explicitly,
- but such disjointness is not to be assumed by default.) For example, it follows
- from the subtype descriptions contained in this chapter that in all
- implementations (typep c 'control-error) implies (typep c 'error), but note
- that (typep c 'control-error) does not imply (not (typep c 'cell-error)).
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.15. Signaling Conditions
-
- [change_begin]
- When a condition is signaled, the system tries to locate the most appropriate
- handler for the condition and to invoke that handler.
-
- Handlers are established dynamically using handler-bind or abstractions built
- on handler-bind.
-
- If an appropriate handler is found, it is called. In some circumstances, the
- handler may decline simply by returning without performing a non-local transfer
- of control. In such cases, the search for an appropriate handler is picked up
- where it left off, as if the called handler had never been present.
-
- If no handler is found, or if all handlers that were found decline, signal
- returns nil.
-
- Although it follows from the description above, it is perhaps worth noting
- explicitly that the lookup procedure described here will prefer a general but
- more (dynamically) local handler over a specific but less (dynamically) local
- handler. Experience with existing condition systems suggests that this is a
- reasonable approach and works adequately in most situations. Some care should
- be taken when binding handlers for very general kinds of conditions, such as is
- done in ignore-errors. Often, binding for a more specific condition type than
- error is more appropriate.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.16. Resignaling Conditions
-
- [change_begin]
- [The contents of this section are still a subject of some debate within X3J13.
- The reader may wish to take this section with a grain of salt.-GLS]
-
- Note that signaling a condition has no side effect on that condition, and that
- there is no dynamic state contained in a condition object. As such, it may at
- times be reasonable and appropriate to consider caching condition objects for
- repeated use, re-signaling conditions from within handlers, or saving
- conditions away somewhere and re-signaling them later.
-
- For example, it may be desirable for the system to pre-allocate objects of type
- storage-condition so that they can be signaled when needed without attempting
- to allocate more storage.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.17. Condition Handlers
-
- [change_begin]
- A handler is a function of one argument, the condition to be handled. The
- handler may inspect the object to be sure it is ``interested'' in handling the
- condition.
-
- A handler is executed in the dynamic context of the signaler, except that the
- set of available condition handlers will have been rebound to the value that
- was active at the time the condition handler was made active. The intent of
- this is to prevent infinite recursion because of errors in a condition handler.
-
- After inspecting the condition, the handler should take one of the following
- actions:
-
- * It might decline to handle the condition (by simply returning). When this
- happens, the returned values are ignored and the effect is the same as if
- the handler had been invisible to the mechanism seeking to find a handler.
- The next handler in line will be tried, or if no such handler exists, the
- condition will go unhandled.
-
- * It might handle the condition (by performing some non-local transfer of
- control). This may be done either primitively using go, return, or throw,
- or more abstractly using a function such as abort or invoke-restart.
-
- * It might signal another condition.
-
- * It might invoke the interactive debugger.
-
- In fact, the latter two actions (signaling another condition or entering the
- debugger) are really just ways of putting off the decision to either handle or
- decline, or trying to get someone else to make such a decision. Ultimately, all
- a handler can do is to handle or decline to handle.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.3.18. Printing Conditions
-
- [change_begin]
- When *print-escape* is nil (for example, when the princ function or the ~A
- directive is used with format), the report method for the condition will be
- invoked. This will be done automatically by functions such as invoke-debugger,
- break, and warn, but there may still be situations in which it is desirable to
- have a condition report under explicit user control. For example,
-
- (let ((form '(open "nosuchfile")))
- (handler-case (eval form)
- (serious-condition (c)
- (format t "~&Evaluation of ~S failed:~%~A" form c))))
-
- might print something like
-
- Evaluation of (OPEN "nosuchfile") failed:
- The file "nosuchfile" was not found.
-
- Some suggestions about the form of text typed by report methods:
-
- * The message should generally be a complete sentence, beginning with a
- capital letter and ending with appropriate punctuation (usually a period).
-
- * The message should not include any introductory text such as ``Error:''
- or ``Warning:'' and should not be followed by a trailing newline. Such
- text will be added as may be appropriate to context by the routine
- invoking the report method.
-
- * Except where unavoidable, the tab character (which is only semi-standard
- anyway) should not be used in error messages. Its effect may vary from one
- implementation to another and may cause problems even within an
- implementation because it may do different things depending on the column
- at which the error report begins.
-
- * Single-line messages are preferred, but newlines in the middle of long
- messages are acceptable.
-
- * If any program (for example, the debugger) displays messages indented
- from the prevailing left margin (for example, indented seven spaces
- because they are prefixed by the seven-character herald ``Error: ''), then
- that program will take care of inserting the appropriate indentation into
- the extra lines of a multi-line error message. Similarly, a program that
- prefixes error messages with semicolons so that they appear to be comments
- should take care of inserting a semicolon at the beginning of each line in
- a multi-line error message. (These rules are important because, even
- within a single implementation, there may be more than one program that
- presents error messages to the user, and they may use different styles of
- presentation. The caller of error cannot anticipate all such possible
- styles, and so it is incumbent upon the presenter of the message to make
- any necessary adjustments.)
-
- [Note: These recommendations expand upon those in section 24.1.-GLS]
-
- When *print-escape* is not nil, the object should print in some useful (but
- usually fairly abbreviated) fashion according to the style of the
- implementation. It is not expected that a condition will be printed in a form
- suitable for read. Something like #<ARITHMETIC-ERROR 1734> is fine.
-
- X3J13 voted in March 1989 (ZLOS-CONDITIONS) to integrate the Condition System
- and the Object System. In the original Condition System proposal, no function
- was provided for directly accessing or setting the printer for a condition
- type, or for invoking it; the techniques described above were the sole
- interface to reporting. The vote specified that, in CLOS terms, condition
- reporting is mediated through the print-object method for the condition type
- (that is, class) in question, with *print-escape* bound to nil. Specifying
- (:report fn) to define-condition when defining condition type C is equivalent
- to a separate method definition:
-
- (defmethod print-object ((x C) stream)
- (if *print-escape*
- (call-next-method)
- (funcall #'fn x stream)))
-
- Note that the method uses fn to print the condition only when *print-escape*
- has the value nil.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4. Program Interface to the Condition System
-
- [change_begin]
- This section describes functions, macros, variables, and condition types
- associated with the Common Lisp Condition System.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- * Signaling Conditions
- * Assertions
- * Exhaustive Case Analysis
- * Handling Conditions
- * Defining Conditions
- * Creating Conditions
- * Establishing Restarts
- * Finding and Manipulating Restarts
- * Warnings
- * Restart Functions
- * Debugging Utilities
-
- -------------------------------------------------------------------------------
-
- 29.4.1. Signaling Conditions
-
- [change_begin]
- The functions in this section provide various mechanisms for signaling
- warnings, breaks, continuable errors, and fatal errors.
-
- [Function]
- error datum &rest arguments
-
- [This supersedes the description of error given in section 24.1.-GLS]
-
- Invokes the signal facility on a condition. If the condition is not handled,
- (invoke-debugger condition) is executed. As a consequence of calling
- invoke-debugger, error never directly returns to its caller; the only exit from
- this function can come by non-local transfer of control in a handler or by use
- of an interactive debugging command.
-
- If datum is a condition, then that condition is used directly. In this case, it
- is an error for the list of arguments to be non-empty; that is, error must have
- been called with exactly one argument, the condition.
-
- If datum is a condition type (a class or class name), then the condition used
- is effectively the result of (apply #'make-condition datum arguments).
-
- If datum is a string, then the condition used is effectively the result of
-
- (make-condition 'simple-error
- :format-string datum
- :format-arguments arguments)
-
- [Function]
- cerror continue-format-string datum &rest arguments
-
- [This supersedes the description of cerror given in section 24.1.-GLS]
-
- The function cerror invokes the error facility on a condition. If the condition
- is not handled, (invoke-debugger condition) is executed. While signaling is
- going on, and while control is in the debugger (if it is reached), it is
- possible to continue program execution (thereby returning from the call to
- cerror) using the continue restart.
-
- If datum is a condition, then that condition is used directly. In this case,
- the list of arguments need not be empty, but will be used only with the
- continue-format-string and will not be used to initialize datum.
-
- If datum is a condition type (a class or class name), then the condition used
- is effectively the result of (apply #'make-condition datum arguments).
-
- If datum is a string, then the condition used is effectively the result of
-
- (make-condition 'simple-error
- :format-string datum
- :format-arguments arguments)
-
- The continue-format-string must be a string. Note that if datum is not a
- string, then the format arguments used by the continue-format-string will still
- be the list of arguments (which is in keyword format if datum is a condition
- type). In this case, some care may be necessary to set up the
- continue-format-string correctly. The format directive ~*, which ignores and
- skips over format arguments, may be particularly useful in this situation.
-
- The value returned by cerror is nil.
-
- [Function]
- signal datum &rest arguments
-
- Invokes the signal facility on a condition. If the condition is not handled,
- signal returns nil.
-
- If datum is a condition, then that condition is used directly. In this case, it
- is an error for the list of arguments to be non-empty; that is, error must have
- been called with exactly one argument, the condition.
-
- If datum is a condition type (a class or class name), then the condition used
- is effectively the result of (apply #'make-condition datum arguments).
-
- If datum is a string, then the condition used is effectively the result of
-
- (make-condition 'simple-error
- :format-string datum
- :format-arguments arguments)
-
- Note that if (typep condition *break-on-signals*) is true, then the debugger
- will be entered prior to beginning the process of signaling. The continue
- restart function may be used to continue with the signaling process; the
- restart is associated with the signaled condition as if by use of
- with-condition-restarts. This is true also for all other functions and macros
- that signal conditions, such as warn, error, cerror, assert, and check-type.
-
- During the dynamic extent of a call to signal with a particular condition, the
- effect of calling signal again on that condition object for a distinct abstract
- event is not defined. For example, although a handler may resignal a condition
- in order to allow outer handlers first shot at handling the condition, two
- distinct asynchronous keyboard events must not signal an the same (eq)
- condition object at the same time.
-
- For further details about signaling and handling, see the discussion of
- condition handlers in section 29.3.17.
-
- [Variable]
- *break-on-signals*
-
- This variable is intended primarily for use when the user is debugging programs
- that do signaling. The value of *break-on-signals* should be suitable as a
- second argument to typep, that is, a type or type specifier.
-
- When (typep condition *break-on-signals*) is true, then calls to signal (and to
- other advertised functions such as error that implicitly call signal) will
- enter the debugger prior to signaling that condition. The continue restart may
- be used to continue with the normal signaling process; the restart is
- associated with the signaled condition as if by use of with-condition-restarts.
-
- Note that nil is a valid type specifier. If the value of *break-on-signals* is
- nil, then signal will never enter the debugger in this implicit manner.
-
- When setting this variable, the user is encouraged to choose the most
- restrictive specification that suffices. Setting this flag effectively violates
- the modular handling of condition signaling that this chapter seeks to
- establish. Its complete effect may be unpredictable in some cases, since the
- user may not be aware of the variety or number of calls to signal that are used
- in programs called only incidentally.
-
- By default-and certainly in any ``production'' use-the value of this variable
- should be nil, both for reasons of performance and for reasons of modularity
- and abstraction.
-
- X3J13 voted in March 1989 (BREAK-ON-WARNINGS-OBSOLETE) to remove
- *break-on-warnings* from the language; *break-on-signals* offers all the power
- of *break-on-warnings* and more.
-
- -------------------------------------------------------------------------------
- Compatibility note: This variable is similar to the Zetalisp variable
- trace-conditions except for the obvious difference that zl:trace-conditions
- takes a type or list of types while *break-on-signals* takes a single type
- specifier.
-
- [There is no loss of generality in Common Lisp because the or type specifier
- may be used to indicate that any of a set of conditions should enter the
- debugger.-GLS]
- -------------------------------------------------------------------------------
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.2. Assertions
-
- [change_begin]
- These facilities are designed to make it convenient for the user to insert
- error checks into code.
-
- [Macro]
- check-type place typespec [string]
-
- [This supersedes the description of check-type given in section 24.2.-GLS]
-
- A check-type form signals an error of type type-error if the contents of place
- are not of the desired type.
-
- If a condition is signaled, handlers of this condition can use the functions
- type-error-datum and type-error-expected-type to access the contents of place
- and the typespec, respectively.
-
- This function can return only if the store-value restart is invoked, either
- explicitly from a handler or implicitly as one of the options offered by the
- debugger. The restart is associated with the signaled condition as if by use of
- with-condition-restarts.
-
- If store-value is called, check-type will store the new value that is the
- argument to store-value (or that is prompted for interactively by the debugger)
- in place and start over, checking the type of the new value and signaling
- another error if it is still not the desired type. Subforms of place may be
- evaluated multiple times because of the implicit loop generated. check-type
- returns nil.
-
- The place must be a generalized variable reference acceptable to setf. The
- typespec must be a type specifier; it is not evaluated. The string should be an
- English description of the type, starting with an indefinite article (``a'' or
- ``an''); it is evaluated. If the string is not supplied, it is computed
- automatically from the typespec. (The optional string argument is allowed
- because some applications of check-type may require a more specific description
- of what is wanted than can be generated automatically from the type specifier.)
-
- The error message will mention the place, its contents, and the desired type.
-
- -------------------------------------------------------------------------------
- Implementation note: An implementation may choose to generate a somewhat
- differently worded error message if it recognizes that place is of a particular
- form, such as one of the arguments to the function that called check-type.
- -------------------------------------------------------------------------------
-
- Lisp> (setq aardvarks '(sam harry fred))
- => (SAM HARRY FRED)
- Lisp> (check-type aardvarks (array * (3)))
- Error: The value of AARDVARKS, (SAM HARRY FRED),
- is not a 3-long array.
- To continue, type :CONTINUE followed by an option number:
- 1: Specify a value to use instead.
- 2: Return to Lisp Toplevel.
- Debug> :continue 1
- Use Value: #(sam fred harry)
- => NIL
- Lisp> aardvarks
- => #<ARRAY-3 13571>
- Lisp> (map 'list #'identity aardvarks)
- => (SAM FRED HARRY)
- Lisp> (setq aacount 'foo)
- => FOO
- Lisp> (check-type aacount (integer 0 *) "a non-negative integer")
- Error: The value of AACOUNT, FOO, is not a non-negative integer.
- To continue, type :CONTINUE followed by an option number:
- 1: Specify a value to use instead.
- 2: Return to Lisp Toplevel.
- Debug> :continue 2
- Lisp>
-
- -------------------------------------------------------------------------------
- Compatibility note: In Zetalisp, the equivalent facility is called
- check-arg-type.
- -------------------------------------------------------------------------------
-
- [Macro]
- assert test-form [({place}*) [datum {argument}*]]
-
- [This supersedes the description of assert given in section 24.2.-GLS]
-
- An assert form signals an error if the value of the test-form is nil.
- Continuing from this error using the continue restart will allow the user to
- alter the values of some variables, and assert will then start over, evaluating
- the test-form again. (The restart is associated with the signaled condition as
- if by use of with-condition-restarts.) assert returns nil.
-
- The test-form may be any form. Each place (there may be any number of them, or
- none) must be a generalized variable reference acceptable to setf. These should
- be variables on which test-form depends, whose values may sensibly be changed
- by the user in attempting to correct the error. Subforms of each place are
- evaluated only if an error is signaled, and may be re-evaluated if the error is
- re-signaled (after continuing without actually fixing the problem).
-
- The datum and arguments are evaluated only if an error is to be signaled, and
- re-evaluated if the error is to be signaled again.
-
- If datum is a condition, then that condition is used directly. In this case, it
- is an error to specify any arguments.
-
- If datum is a condition type (a class or class name), then the condition used
- is effectively the result of (apply #'make-condition datum (list argument)).
-
- If datum is a string, then the condition used is effectively the result of
-
- (make-condition 'simple-error
- :format-string datum
- :format-arguments (list argument))
-
- If datum is omitted, then a condition of type simple-error is constructed using
- the test-form as data. For example, the following might be used:
-
- (make-condition 'simple-error
- :format-string "The assertion ~S failed."
- :format-arguments '(test-form))
-
- Note that the test-form itself, and not its value, is used as the format
- argument.
-
- -------------------------------------------------------------------------------
- Implementation note: The debugger need not include the test-form in the error
- message, and any places should not be included in the message, but they should
- be made available for the user's perusal. If the user gives the ``continue''
- command, an opportunity should be presented to alter the values of any or all
- of the references. The details of this depend on the implementation's style of
- user interface, of course.
- -------------------------------------------------------------------------------
-
- Here is an example of the use of assert:
-
- (setq x (make-array '(3 5) :initial-element 3))
- (setq y (make-array '(3 5) :initial-element 7))
-
- (defun matrix-multiply (a b)
- (let ((*print-array* nil))
- (assert (and (= (array-rank a) (array-rank b) 2)
- (= (array-dimension a 1)
- (array-dimension b 0)))
- (a b)
- "Cannot multiply ~S by ~S." a b)
- (really-matrix-multiply a b)))
-
- (matrix-multiply x y)
- Error: Cannot multiply #<ARRAY-3-5 12345> by #<ARRAY-3-5 12364>.
- To continue, type :CONTINUE followed by an option number:
- 1: Specify new values.
- 2: Return to Lisp Toplevel.
- Debug> :continue 1
- Value for A: x
- Value for B: (make-array '(5 3) :initial-element 6)
- => #2A(Ø(54 54 54 54 54)
- (54 54 54 54 54)
- (54 54 54 54 54)
- (54 54 54 54 54)
- (54 54 54 54 54))
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.3. Exhaustive Case Analysis
-
- [change_begin]
- The syntax for etypecase and ctypecase is the same as for typecase, except that
- no otherwise clause is permitted. Similarly, the syntax for ecase and ccase is
- the same as for case except for the otherwise clause.
-
- etypecase and ecase are similar to typecase and case, respectively, but signal
- a non-continuable error rather than returning nil if no clause is selected.
-
- ctypecase and ccase are also similar to typecase and case, respectively, but
- signal a continuable error if no clause is selected.
-
- [Macro]
- etypecase keyform {(type {form}*)}*
-
- [This supersedes the description of etypecase given in section 24.3.-GLS]
-
- This control construct is similar to typecase, but no explicit otherwise or t
- clause is permitted. If no clause is satisfied, etypecase signals an error (of
- type type-error) with a message constructed from the clauses. It is not
- permissible to continue from this error. To supply an error message, the user
- should use typecase with an otherwise clause containing a call to error. The
- name of this function stands for ``exhaustive type case'' or ``error-checking
- type case.''
-
- Example:
-
- Lisp> (setq x 1/3)
- => 1/3
- Lisp> (etypecase x
- (integer (* x 4))
- (symbol (symbol-value x)))
- Error: The value of X, 1/3, is neither an integer nor a symbol.
- To continue, type :CONTINUE followed by an option number:
- 1: Return to Lisp Toplevel.
- Debug>
-
- [Macro]
- ctypecase keyplace {(type {form}*)}*
-
- [This supersedes the description of ctypecase given in section 24.3.-GLS]
-
- This control construct is similar to typecase, but no explicit otherwise or t
- clause is permitted.
-
- The keyplace must be a generalized variable reference acceptable to setf. If no
- clause is satisfied, ctypecase signals an error (of type type-error) with a
- message constructed from the clauses. This error may be continued using the
- store-value restart. The argument to store-value is stored in keyplace and then
- ctypecase starts over, making the type tests again. Subforms of keyplace may be
- evaluated multiple times. If the store-value restart is invoked interactively,
- the user will be prompted for the value to be used.
-
- The name of this function is mnemonic for ``continuable (exhaustive) type
- case.''
-
- Example:
-
- Lisp> (setq x 1/3)
- => 1/3
- Lisp> (ctypecase x
- (integer (* x 4))
- (symbol (symbol-value x)))
- Error: The value of X, 1/3, is neither an integer nor a symbol.
- To continue, type :CONTINUE followed by an option number:
- 1: Specify a value to use instead.
- 2: Return to Lisp Toplevel.
- Debug> :continue 1
- Use value: 3.7
- Error: The value of X, 3.7, is neither an integer nor a symbol.
- To continue, type :CONTINUE followed by an option number:
- 1: Specify a value to use instead.
- 2: Return to Lisp Toplevel.
- Debug> :continue 1
- Use value: 12
- => 48
-
- [Macro]
- ecase keyform {({({key}*) | key} {form}*)}*
-
- [This supersedes the description of ecase given in section 24.3.-GLS]
-
- This control construct is similar to case, but no explicit otherwise or t
- clause is permitted. If no clause is satisfied, ecase signals an error (of type
- type-error) with a message constructed from the clauses. It is not permissible
- to continue from this error. To supply an error message, the user should use
- case with an otherwise clause containing a call to error. The name of this
- function stands for ``exhaustive case'' or ``error-checking case.''
-
- Example:
-
- Lisp> (setq x 1/3)
- => 1/3
- Lisp> (ecase x
- (alpha (foo))
- (omega (bar))
- ((zeta phi) (baz)))
- Error: The value of X, 1/3, is not ALPHA, OMEGA, ZETA, or PHI.
- To continue, type :CONTINUE followed by an option number:
- 1: Return to Lisp Toplevel.
- Debug>
-
- [Macro]
- ccase keyplace {({({key}*) | key} {form}*)}*
-
- [This supersedes the description of ccase given in section 24.3.-GLS]
-
- This control construct is similar to case, but no explicit otherwise or t
- clause is permitted.
-
- The keyplace must be a generalized variable reference acceptable to setf. If no
- clause is satisfied, ccase signals an error (of type type-error) with a message
- constructed from the clauses. This error may be continued using the store-value
- restart. The argument to store-value is stored in keyplace and then ccase
- starts over, making the type tests again. Subforms of keyplace may be evaluated
- multiple times. If the store-value restart is invoked interactively, the user
- will be prompted for the value to be used.
-
- The name of this function is mnemonic for ``continuable (exhaustive) case.''
-
- -------------------------------------------------------------------------------
- Implementation note: The type-error signaled by ccase and ecase is free to
- choose any representation of the acceptable argument type that it wishes for
- placement in the expected-type slot. It will always work to use type (member .
- keys), but in some cases it may be more efficient, for example, to use a type
- that represents an integer subrange or a type composed using the or type
- specifier.
- -------------------------------------------------------------------------------
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.4. Handling Conditions
-
- [change_begin]
- These macros allow a program to gain control when a condition is signaled.
-
- [Macro]
- handler-case expression {(typespec ([var]) {form}*)}*
-
- Executes the given expression in a context where various specified handlers are
- active.
-
- Each typespec may be any type specifier. If during the execution of the
- expression a condition is signaled for which there is an appropriate
- clause-that is, one for which (typep condition 'typespec) is true-and if there
- is no intervening handler for conditions of that type, then control is
- transferred to the body of the relevant clause (unwinding the dynamic state
- appropriately in the process) and the given variable var is bound to the
- condition that was signaled. If no such condition is signaled and the
- computation runs to completion, then the values resulting from the expression
- are returned by the handler-case form.
-
- If more than one case is provided, those cases are made accessible in parallel.
- That is, in
-
- (handler-case expression
- (type1 (var1) form1)
- (type2 (var2) form2))
-
- if the first clause (containing form1) has been selected, the handler for the
- second is no longer visible (and vice versa).
-
- The cases are searched sequentially from top to bottom. If a signaled condition
- matches more than one case (possible if there is type overlap) the earlier of
- the two cases will be selected.
-
- If the variable var is not needed, it may be omitted. That is, a clause such as
-
- (type (var) (declare (ignore var)) form)
-
- may be written using the following shorthand notation:
-
- (type () form)
-
- If there are no forms in a selected case, the case returns nil. Note that
-
- (handler-case expression
- (type1 (var1) . body1)
- (type2 (var2) . body2)
- ...)
-
- is approximately equivalent to
-
- (block #1=#:block-1
- (let (#2=#:var-2)
- (tagbody
- (handler-bind ((type1 Ø#'(lambda (temp)
- (setq #2# temp)
- (go #3=#:tag-3)))
- (type2 Ø#'(lambda (temp)
- (setq #2# temp)
- (go #4=#:tag-4)))
- ...)
- (return-from #1# expression))
- #3# (return-from #1# (let ((var1 #2#)) . body1))
- #4# (return-from #1# (let ((var2 #2#)) . body2))
- ...)))
-
- [Note the use of ``gensyms'' such as #:block-1 as block names, variables, and
- tagbody tags in this example, and the use of #n= and #n# read-macro syntax to
- indicate that the very same gensym appears in multiple places.-GLS]
-
- As a special case, the typespec can also be the symbol :no-error in the last
- clause. If it is, it designates a clause that will take control if the
- expression returns normally. In that case, a completely general lambda-list may
- follow the symbol :no-error, and the arguments to which the lambda-list
- parameters are bound are like those for multiple-value-call on the return value
- of the expression. For example,
-
- (handler-case expression
- (type1 (var1) . body1)
- (type2 (var2) . body2)
- ...
- (typen (varn) . bodyn)
- (:no-error (nvar1 nvar2 ... nvarm) . nbody))
-
- is approximately equivalent to
-
- (block #1=#:error-return
- (multiple-value-call #'(lambda (nvar1 nvar2 ... nvarm) . nbody)
- (block #2=#:normal-return
- (return-from #1#
- (handler-case (return-from #2# expression)
- (type1 (var1) . body1)
- (type2 (var2) . body2)
- ...
- (typen (varn) . bodyn))))))
-
- Examples of the use of handler-case:
-
- (handler-case (/ x y)
- (division-by-zero () nil))
-
- (handler-case (open *the-file* :direction :input)
- (file-error (condition) (format t "~&Fooey: ~A~%" condition)))
-
- (handler-case (some-user-function)
- (file-error (condition) condition)
- (division-by-zero () 0)
- ((or unbound-variable undefined-function) () 'unbound))
-
- (handler-case (intern x y)
- (error (condition) condition)
- (:no-error (symbol status)
- (declare (ignore symbol))
- status))
-
- [Macro]
- ignore-errors {form}*
-
- Executes its body in a context that handles conditions of type error by
- returning control to this form. If no such condition is signaled, any values
- returned by the last form are returned by ignore-errors. Otherwise, two values
- are returned: nil and the error condition that was signaled.
-
- ignore-errors could be defined by
-
- (defmacro ignore-errors (&body forms)
- `(handler-case (progn ,@forms)
- (error (c) (values nil c)))
-
- [Macro]
- handler-bind ({(typespec handler)}*) {form}*
-
- Executes body in a dynamic context where the given handler bindings are in
- effect. Each typespec may be any type specifier. Each handler form should
- evaluate to a function to be used to handle conditions of the given type(s)
- during execution of the forms. This function should take a single argument, the
- condition being signaled.
-
- If more than one binding is specified, the bindings are searched sequentially
- from top to bottom in search of a match (by visual analogy with typecase). If
- an appropriate typespec is found, the associated handler is run in a context
- where none of the handler bindings are visible (to avoid recursive errors). For
- example, in the case of
-
- (handler-bind ((unbound-variable #'(lambda ...))
- (error #'(lambda ...)))
- ...)
-
- if an unbound variable error is signaled in the body (and not handled by an
- intervening handler), the first function will be called. If any other kind of
- error is signaled, the second function will be called. In either case, neither
- handler will be active while executing the code in the associated function.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.5. Defining Conditions
-
- [change_begin]
- [The contents of this section are still a subject of some debate within X3J13.
- The reader may wish to take this section with a grain of salt, two aspirin
- tablets, and call a hacker in the morning.-GLS]
-
- [Macro]
-
- define-condition name ({parent-type}*)
- [({slot-specifier}*) {option}*]
-
- Defines a new condition type called name, which is a subtype of each given
- parent-type. Except as otherwise noted, the arguments are not evaluated.
-
- Objects of this condition type will have all of the indicated slots, plus any
- additional slots inherited from the parent types (its superclasses). If the
- slots list is omitted, the empty list is assumed.
-
- A slot must have the form
-
- slot-specifier ::= slot-name | (slot-name [[?slot-option]])
-
- For the syntax of a slot-option, see defclass. The slots of a condition object
- are normal CLOS slots. Note that with-slots may be used instead of accessor
- functions to access slots of a condition object.
-
- make-condition will accept keywords (in the keyword package) with the print
- name of any of the designated slots, and will initialize the corresponding
- slots in conditions it creates.
-
- Accessors are created according to the same rules as used by defclass.
-
- The valid options are as follows:
-
- (:documentation doc-string)
-
- The doc-string should be either nil or a string that describes the purpose
- of the condition type. If this option is omitted, nil is assumed. Calling
- (documentation 'name 'type) will retrieve this information.
-
- (:report exp)
-
- If exp is not a literal string, it must be a suitable argument to the
- function special form. The expression (function exp) will be evaluated in
- the current lexical environment. It should produce a function of two
- arguments, a condition and a stream, that prints on the stream a
- description of the condition. This function is called whenever the
- condition is printed while *print-escape* is nil.
-
- If exp is a literal string, it is shorthand for
-
- (lambda (c s)
- (declare (ignore c))
- (write-string exp s))
-
- [That is, a function is provided that will simply write the given string
- literally to the stream, regardless of the particular condition object
- supplied.-GLS]
-
- The :report option is processed after the new condition type has been
- defined, so use of the slot accessors within the report function is
- permitted. If this option is not specified, information about how to
- report this type of condition will be inherited from the parent-type.
-
- [X3J13 voted in March 1989 (ZLOS-CONDITIONS) to integrate the Condition
- System and the Object System. In the original Condition System proposal,
- define-condition allowed only one parent-type (the inheritance structure was a
- simple hierarchy). Slot descriptions were much simpler, even simpler than those
- for defstruct:
-
- slot ::= slot-name | (slot-name) | (slot-name default-value)
-
- Similarly, define-condition allowed a :conc-name option similar to that of
- defstruct:
-
- (:conc-name symbol-or-string)
-
- Not now part of Common Lisp. As with defstruct, this sets up automatic
- prefixing of the names of slot accessors. Also as in defstruct, the
- default behavior is to use the name of the new type, name, followed by a
- hyphen. (Generated names are interned in the package that is current at
- the time that the define-condition is processed).
-
- One consequence of the vote was to make define-condition slot descriptions like
- those of defclass.-GLS]
-
- Here are some examples of the use of define-condition.
-
- The following form defines a condition of type peg/hole-mismatch that inherits
- from a condition type called blocks-world-error:
-
- (define-condition peg/hole-mismatch (blocks-world-error)
- (peg-shape hole-shape)
- (:report
- (lambda (condition stream)
- (with-slots (peg-shape hole-shape) condition
- (format stream "A ~A peg cannot go in a ~A hole."
- peg-shape hole-shape))))
-
- The new type has slots peg-shape and hole-shape, so make-condition will accept
- :peg-shape and :hole-shape keywords. The with-slots macro may be used to access
- the peg-shape and hole-shape slots, as illustrated in the :report information.
-
- Here is another example. This defines a condition called machine-error that
- inherits from error:
-
- (define-condition machine-error (error)
- ((machine-name
- :reader machine-error-machine-name))
- (:report (lambda (condition stream)
- (format stream "There is a problem with ~A."
- (machine-error-machine-name condition)))))
-
- Building on this definition, we can define a new error condition that is a
- subtype of machine-error for use when machines are not available:
-
- (define-condition machine-not-available-error (machine-error) ()
- (:report (lambda (condition stream)
- (format stream "The machine ~A is not available."
- (machine-error-machine-name condition)))))
-
- We may now define a still more specific condition, built upon
- machine-not-available-error, that provides a default for machine-name but does
- not provide any new slots or report information. It just gives the machine-name
- slot a default initialization:
-
- (define-condition my-favorite-machine-not-available-error
- (machine-not-available-error)
- ((machine-name :initform "MC.LCS.MIT.EDU")))
-
- Note that since no :report clause was given, the information inherited from
- machine-not-available-error will be used to report this type of condition.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.6. Creating Conditions
-
- [change_begin]
- The function make-condition is the basic means for creating condition objects.
-
- [Function]
- make-condition type &rest slot-initializations
-
- Constructs a condition object of the given type using slot-initializations as a
- specification of the initial value of the slots. The newly created condition is
- returned.
-
- The slot-initializations are alternating keyword/value pairs. For example:
-
- (make-condition 'peg/hole-mismatch
- :peg-shape 'square :hole-shape 'round)
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.7. Establishing Restarts
-
- [change_begin]
- The lowest-level form that creates restart points is called restart-bind. The
- restart-case macro is an abstraction that addresses many common needs for
- restart-bind while offering a more palatable syntax. See also
- with-simple-restart. The function that transfers control to a restart point
- established by one of these macros is called invoke-restart.
-
- All restarts have dynamic extent; a restart does not survive execution of the
- form that establishes it.
-
- [Macro]
-
- with-simple-restart (name format-string {format-argument}*)
- {form}*
-
- This is shorthand for one of the most common uses of restart-case.
-
- If the restart designated by name is not invoked while executing the forms, all
- values returned by the last form are returned. If that restart is invoked,
- control is transferred to the with-simple-restart form, which immediately
- returns the two values nil and t.
-
- The name may be nil, in which case an anonymous restart is established.
-
- with-simple-restart could be defined by
-
- (defmacro with-simple-restart ((restart-name format-string
- &rest format-arguments)
- &body forms)
- `(restart-case (progn ,@forms)
- (,restart-name ()
- :report
- (lambda (stream)
- (format stream ,format-string ,@format-arguments))
- (values nil t))))
-
- Here is an example of the use of with-simple-restart.
-
- Lisp> (defun read-eval-print-loop (level)
- (with-simple-restart
- (abort "Exit command level ~D." level)
- (loop
- (with-simple-restart
- (abort "Return to command level ~D." level)
- (let ((form (prog2 (fresh-line)
- (read)
- (fresh-line))))
- (prin1 (eval form)))))))
- => READ-EVAL-PRINT-LOOP
- Lisp> (read-eval-print-loop 1)
- (+ 'a 3)
- Error: The argument, A, to the function + was of the wrong type.
- The function expected a number.
- To continue, type :CONTINUE followed by an option number:
- 1: Specify a value to use this time.
- 2: Return to command level 1.
- 3: Exit command level 1.
- 4: Return to Lisp Toplevel.
- Debug>
-
- -------------------------------------------------------------------------------
- Compatibility note: In contrast to the way that Zetalisp has traditionally
- defined abort as a kind of condition to be handled, the Common Lisp Condition
- System defines abort as a way to restart (``proceed'' in Zetalisp terms).
- -------------------------------------------------------------------------------
- Remark: Some readers may wonder what ought to be done by the ``abort'' key (or
- whatever the implementation's interrupt key is-Control-C or Control-G, for
- example). Such interrupts, whether synchronous or asynchronous in nature, are
- beyond the scope of this chapter and indeed are not currently addressed by
- Common Lisp at all. This may be a topic worth standardizing under separate
- cover. Here is some speculation about some possible things that might happen.
-
- An implementation might simply call abort or break directly without signaling
- any condition.
-
- Another implementation might signal some condition related to the fact that a
- key had been pressed rather than to the action that should be taken. This is
- one way to allow user customization. Perhaps there would be an
- implementation-dependent keyboard-interrupt condition type with a slot
- containing the key that was pressed-or perhaps there would be such a condition
- type, but rather than its having slots, different subtypes of that type with
- names like keyboard-abort, keyboard-break, and so on might be signaled. That
- implementation would then document the action it would take if user programs
- failed to handle the condition, and perhaps ways for user programs to usefully
- dismiss the interrupt.
- -------------------------------------------------------------------------------
- Implementation note: Implementors are encouraged to make sure that there is
- always a restart named abort around any user code so that user code can call
- abort at any time and expect something reasonable to happen; exactly what the
- reasonable thing is may vary somewhat. Typically, in an interactive program,
- invoking abort should return the user to top level, though in some batch or
- multi-processing situations killing the running process might be more
- appropriate.
- -------------------------------------------------------------------------------
-
- [Macro]
-
- restart-case expression {(case-name arglist
- {keyword value}*
- {form}*)}*
-
- The expression is evaluated in a dynamic context where the clauses have special
- meanings as points to which control may be transferred. If the expression
- finishes executing and returns any values, all such values are simply returned
- by the restart-case form. While the expression is running, any code may
- transfer control to one of the clauses (see invoke-restart). If a transfer
- occurs, the forms in the body of that clause will be evaluated and any values
- returned by the last such form will be returned by the restart-case form.
-
- As a special case, if the expression is a list whose car is signal, error,
- cerror, or warn, then with-condition-restarts is implicitly used to associate
- the restarts with the condition to be signaled. For example,
-
- (restart-case (signal weird-error)
- (become-confused ...)
- (rewind-line-printer ...)
- (halt-and-catch-fire ...))
-
- is equivalent to
-
- (restart-case (with-condition-restarts
- weird-error
- (list (find-restart 'become-confused)
- (find-restart 'rewind-line-printer)
- (find-restart 'halt-and-catch-fire))
- (signal weird-error))
- (become-confused ...)
- (rewind-line-printer ...)
- (halt-and-catch-fire ...))
-
- If there are no forms in a selected clause, restart-case returns nil.
-
- The case-name may be nil or a symbol naming this restart.
-
- It is possible to have more than one clause use the same case-name. In this
- case, the first clause with that name will be found by find-restart. The other
- clauses are accessible using compute-restarts. [In this respect, restart-case
- is rather different from case!-GLS]
-
- Each arglist is a normal lambda-list containing parameters to be bound during
- the execution of its corresponding forms. These parameters are used to pass any
- necessary data from a call to invoke-restart to the restart-case clause.
-
- By default, invoke-restart-interactively will pass no arguments and all
- parameters must be optional in order to accommodate interactive restarting.
- However, the parameters need not be optional if the :interactive keyword has
- been used to inform invoke-restart-interactively about how to compute a proper
- argument list.
-
- The valid keyword value pairs are the following:
-
- :test fn
-
- The fn must be a suitable argument for the function special form. The
- expression (function fn) will be evaluated in the current lexical
- environment. It should produce a function of one argument, a condition. If
- this function returns nil when given some condition, functions such as
- find-restart, compute-restart, and invoke-restart will not consider this
- restart when searching for restarts associated with that condition. If
- this pair is not supplied, it is as if
-
- (lambda (c) (declare (ignore c)) t)
-
- were used for the fn.
-
- :interactive fn
-
- The fn must be a suitable argument for the function special form. The
- expression (function fn) will be evaluated in the current lexical
- environment. It should produce a function of no arguments that returns
- arguments to be used by invoke-restart-interactively when invoking this
- function. This function will be called in the dynamic environment
- available prior to any restart attempt. It may interact with the user on
- the stream in *query-io*.
-
- If a restart is invoked interactively but no :interactive option was
- supplied, the argument list used in the invocation is the empty list.
-
- :report exp
-
- If exp is not a literal string, it must be a suitable argument to the
- function special form. The expression (function exp) will be evaluated in
- the current lexical environment. It should produce a function of one
- argument, a stream, that prints on the stream a description of the
- restart. This function is called whenever the restart is printed while
- *print-escape* is nil.
-
- If exp is a literal string, it is shorthand for
-
- (lambda (s) (write-string exp s))
-
- [That is, a function is provided that will simply write the given string
- literally to the stream.-GLS]
-
- If a named restart is asked to report but no report information has been
- supplied, the name of the restart is used in generating default report
- text.
-
- When *print-escape* is nil, the printer will use the report information
- for a restart. For example, a debugger might announce the action of typing
- ``:continue'' by executing the equivalent of
-
- (format *debug-io* "~&~S - ~A~%" ':continue some-restart)
-
- which might then display as something like
-
- :CONTINUE - Return to command level.
-
- It is an error if an unnamed restart is used and no report information is
- provided.
-
- -------------------------------------------------------------------------------
- Rationale: Unnamed restarts are required to have report information on the
- grounds that they are generally only useful interactively, and an interactive
- option that has no description is of little value.
- -------------------------------------------------------------------------------
- Implementation note: Implementations are encouraged to warn about this error at
- compilation time.
-
- At run time, this error might be noticed when entering the debugger. Since
- signaling an error would probably cause recursive entry into the debugger
- (causing yet another recursive error, and so on), it is suggested that the
- debugger print some indication of such problems when they occur, but not
- actually signal errors.
- -------------------------------------------------------------------------------
-
- Note that
-
- (restart-case expression
- (name1 arglist1 options1 . body1)
- (name2 arglist2 options2 . body2)
- ...)
-
- is essentially equivalent to
-
- (block #1=#:block-1
- (let ((#2=#:var-2 nil))
- (tagbody
- (restart-bind ((name1 #'(lambda (&rest temp)
- (setq #2# temp)
- (go #3=#:tag-3))
- <slightly transformed options1>)
- (name2 #'(lambda (&rest temp)
- (setq #2# temp)
- (go #4=#:tag-4))
- <slightly transformed options2>)
- ...)
- (return-from #1# expression))
- #3# (return-from #1#
- (apply #'(lambda arglist1 . body1) #2#))
- #4# (return-from #1#
- (apply #'(lambda arglist2 . body2) #2#))
- ...)))
-
- [Note the use of ``gensyms'' such as #:block-1 as block names, variables, and
- tagbody tags in this example, and the use of #n= and #n# read-macro syntax to
- indicate that the very same gensym appears in multiple places.-GLS]
-
- Here are some examples of the use of restart-case.
-
- (loop
- (restart-case (return (apply function some-args))
- (new-function (new-function)
- :report "Use a different function."
- :interactive
- (lambda ()
- (list (prompt-for 'function "Function: ")))
- (setq function new-function))))
-
- (loop
- (restart-case (return (apply function some-args))
- (nil (new-function)
- :report "Use a different function."
- :interactive
- (lambda ()
- (list (prompt-for 'function "Function: ")))
- (setq function new-function))))
-
- (restart-case (a-command-loop)
- (return-from-command-level ()
- :report
- (lambda (s) ;Argument s is a stream
- (format s "Return from command level ~D." level))
- nil))
-
- (loop
- (restart-case (another-random-computation)
- (continue () nil)))
-
- The first and second examples are equivalent from the point of view of someone
- using the interactive debugger, but they differ in one important aspect for
- non-interactive handling. If a handler ``knows about'' named restarts, as in,
- for example,
-
- (when (find-restart 'new-function)
- (invoke-restart 'new-function the-replacement))
-
- then only the first example, and not the second, will have control transferred
- to its correction clause, since only the first example uses a restart named
- new-function.
-
- Here is a more complete example:
-
- (let ((my-food 'milk)
- (my-color 'greenish-blue))
- (do ()
- ((not (bad-food-color-p my-food my-color)))
- (restart-case (error 'bad-food-color
- :food my-food :color my-color)
- (use-food (new-food)
- :report "Use another food."
- (setq my-food new-food))
- (use-color (new-color)
- :report "Use another color."
- (setq my-color new-color))))
- ;; We won't get to here until MY-FOOD
- ;; and MY-COLOR are compatible.
- (list my-food my-color))
-
- Assuming that use-food and use-color have been defined as
-
- (defun use-food (new-food)
- (invoke-restart 'use-food new-food))
-
- (defun use-color (new-color)
- (invoke-restart 'use-color new-color))
-
- a handler can then restart from the error in either of two ways. It may correct
- the color or correct the food. For example:
-
- #'(lambda (c) ... (use-color 'white) ...) ;Corrects color
-
- #'(lambda (c) ... (use-food 'cheese) ...) ;Corrects food
-
- Here is an example using handler-bind and restart-case that refers to a
- condition type foo-error, presumably defined elsewhere:
-
- (handler-bind ((foo-error #'(lambda (ignore) (use-value 7))))
- (restart-case (error 'foo-error)
- (use-value (x) (* x x))))
- => 49
-
- [Macro]
- restart-bind ({(name function {keyword value}*)}*) {form}*
-
- Executes a body of forms in a dynamic context where the given restart bindings
- are in effect.
-
- Each name may be nil to indicate an anonymous restart, or some other symbol to
- indicate a named restart.
-
- Each function is a form that should evaluate to a function to be used to
- perform the restart. If invoked, this function may either perform a non-local
- transfer of control or it may return normally. The function may take whatever
- arguments the programmer feels are appropriate; it will be invoked only if
- invoke-restart is used from a program, or if a user interactively asks the
- debugger to invoke it. In the case of interactive invocation, the
- :interactive-function option is used.
-
- The valid keyword value pairs are as follows:
-
- :test-function form
-
- The form will be evaluated in the current lexical environment and should
- return a function of one argument, a condition. If this function returns
- nil when given some condition, functions such as find-restart,
- compute-restart, and invoke-restart will not consider this restart when
- searching for restarts associated with that condition. If this pair is not
- supplied, it is as if
-
- #'(lambda (c) (declare (ignore c)) t)
-
- were used for the form.
-
- :interactive-function form
-
- The form will be evaluated in the current lexical environment and should
- return a function of no arguments that constructs a list of arguments to
- be used by invoke-restart-interactively when invoking this restart. The
- function may prompt interactively using *query-io* if necessary.
-
- :report-function form
-
- The form will be evaluated in the current lexical environment and should
- return a function of one argument, a stream, that prints on the stream a
- summary of the action this restart will take. This function is called
- whenever the restart is printed while *print-escape* is nil.
-
- [Macro]
-
- with-condition-restarts condition-form restarts-form
- {declaration}* {form}*
-
- The value of condition-form should be a condition C and the value of
- restarts-form should be a list of restarts (R1 R2 ...). The forms of the body
- are evaluated as an implicit progn. While in the dynamic context of the body,
- an attempt to find a restart associated with a particular condition C' will
- consider the restarts R1, R2, ... if C' is eq to C.
-
- Usually this macro is not used explicitly in code, because restart-case handles
- most of the common uses in a way that is syntactically more concise.
-
- [The X3J13 vote (CONDITION-RESTARTS) left it unclear whether
- with-condition-restarts permits declarations to appear at the heads of its
- body. I believe that was the intent, but this is only my interpretation.-GLS]
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.8. Finding and Manipulating Restarts
-
- [change_begin]
- The following functions determine what restarts are active and invoke restarts.
-
- [Function]
- compute-restarts &optional condition
-
- Uses the dynamic state of the program to compute a list of the restarts that
- are currently active. See restart-bind.
-
- If condition is nil or not supplied, all outstanding restarts are returned. If
- condition is not nil, only restarts associated with that condition are
- returned.
-
- Each restart represents a function that can be called to perform some form of
- recovery action, usually a transfer of control to an outer point in the running
- program. Implementations are free to implement these objects in whatever manner
- is most convenient; the objects need have only dynamic extent (relative to the
- scope of the binding form that instantiates them).
-
- The list that results from a call to compute-restarts is ordered so that the
- inner (that is, more recently established) restarts are nearer the head of the
- list.
-
- Note, too, that compute-restarts returns all valid restarts, including
- anonymous ones, even if some of them have the same name as others and would
- therefore not be found by find-restart when given a symbol argument.
-
- Implementations are permitted, but not required, to return different (that is,
- non-eq) lists from repeated calls to compute-restarts while in the same dynamic
- environment. It is an error to modify the list that is returned by
- compute-restarts.
-
- [Function]
- restart-name restart
-
- Returns the name of the given restart, or nil if it is not named.
-
- [Function]
- find-restart restart-identifier &optional condition
-
- Searches for a particular restart in the current dynamic environment.
-
- If condition is nil or not supplied, all outstanding restarts are considered.
- If condition is not nil, only restarts associated with that condition are
- considered.
-
- If the restart-identifier is a non-nil symbol, then the innermost (that is,
- most recently established) restart with that name is returned; nil is returned
- if no such restart is found.
-
- If restart-identifier is a restart object, then it is simply returned, unless
- it is not currently active, in which case nil is returned.
-
- Although anonymous restarts have a name of nil, it is an error for the symbol
- nil to be given as the restart-identifier. Applications that would seem to
- require this should be rewritten to make appropriate use of compute-restarts
- instead.
-
- [Function]
- invoke-restart restart-identifier &rest arguments
-
- Calls the function associated with the given restart-identifier, passing any
- given arguments. The restart-identifier must be a restart or the non-null name
- of a restart that is valid in the current dynamic context. If the argument is
- not valid, an error of type control-error will be signaled.
-
- -------------------------------------------------------------------------------
- Implementation note: Restart functions call this function, not vice versa.
- -------------------------------------------------------------------------------
-
- [Function]
- invoke-restart-interactively restart-identifier
-
- Calls the function associated with the given restart-identifier, prompting for
- any necessary arguments. The restart-identifier must be a restart or the
- non-null name of a restart that is valid in the current dynamic context. If the
- argument is not valid, an error of type control-error will be signaled.
-
- The function invoke-restart-interactively will prompt for arguments by
- executing the code provided in the :interactive keyword to restart-case or
- :interactive-function keyword to restart-bind.
-
- If no :interactive or :interactive-function option has been supplied in the
- corresponding restart-case or restart-bind, then it is an error if the restart
- takes required arguments. If the arguments are optional, an empty argument list
- will be used in this case.
-
- Once invoke-restart-interactively has calculated the arguments, it simply
- performs (apply #'invoke-restart restart-identifier arguments).
-
- invoke-restart-interactively is used internally by the debugger and may also be
- useful in implementing other portable, interactive debugging tools.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.9. Warnings
-
- [change_begin]
- Warnings are a subclass of errors that are conventionally regarded as ``mild.''
-
- [Function]
- warn datum &rest arguments
-
- [This supersedes the description of warn given in section 24.1.-GLS]
-
- Warns about a situation, by signaling a condition of type warning.
-
- If datum is a condition, then that condition is used directly. In this case, if
- the condition is not of type warning or arguments is non-nil, an error of type
- type-error is signaled.
-
- If datum is a condition type (a class or class name), then the condition used
- is effectively the result of (apply #'make-condition datum arguments). This
- result must be of type warning or an error of type type-error is signaled.
-
- If datum is a string, then the condition used is effectively the result of
-
- (make-condition 'simple-error
- :format-string datum
- :format-arguments arguments)
-
- The precise mechanism for warning is as follows.
-
- 1. The warning condition is signaled.
-
- While the warning condition is being signaled, the muffle-warning restart
- is established for use by a handler to bypass further action by warn (that
- is, to cause warn to immediately return nil).
-
- As part of the signaling process, if (typep condition *break-on-signals*)
- is true, then a break will occur prior to beginning the signaling process.
-
- 2. If no handlers for the warning condition are found, or if all such
- handlers decline, then the condition will be reported to *error-output* by
- the warn function (with possible implementation-specific extra output such
- as motion to a fresh line before or after the display of the warning, or
- supplying some introductory text mentioning the name of the function that
- called warn or the fact that this is a warning).
-
- 3. The value returned by warn (if it returns) is nil.
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.10. Restart Functions
-
- [change_begin]
- Common Lisp has the following restart functions built in.
-
- [Function]
- abort &optional condition
-
- This function transfers control to the restart named abort. If no such restart
- exists, abort signals an error of type control-error.
-
- If condition is nil or not supplied, all outstanding restarts are considered.
- If condition is not nil, only restarts associated with that condition are
- considered.
-
- The purpose of the abort restart is generally to allow control to return to the
- innermost ``command level.''
-
- [Function]
- continue &optional condition
-
- This function transfers control to the restart named continue. If no such
- restart exists, continue returns nil.
-
- If condition is nil or not supplied, all outstanding restarts are considered.
- If condition is not nil, only restarts associated with that condition are
- considered.
-
- The continue restart is generally part of simple protocols where there is a
- single ``obvious'' way to continue, as with break and cerror. Some user-defined
- protocols may also wish to incorporate it for similar reasons. In general,
- however, it is more reliable to design a special-purpose restart with a name
- that better suits the particular application.
-
- [Function]
- muffle-warning &optional condition
-
- This function transfers control to the restart named muffle-warning. If no such
- restart exists, muffle-warning signals an error of type control-error.
-
- If condition is nil or not supplied, all outstanding restarts are considered.
- If condition is not nil, only restarts associated with that condition are
- considered.
-
- warn sets up this restart so that handlers of warning conditions have a way to
- tell warn that a warning has already been dealt with and that no further action
- is warranted.
-
- [Function]
- store-value value &optional condition
-
- This function transfers control (and one value) to the restart named
- store-value. If no such restart exists, store-value returns nil.
-
- If condition is nil or not supplied, all outstanding restarts are considered.
- If condition is not nil, only restarts associated with that condition are
- considered.
-
- The store-value restart is generally used by handlers trying to recover from
- errors of types such as cell-error or type-error, where the handler may wish to
- supply a replacement datum to be stored permanently.
-
- [Function]
- use-value value &optional condition
-
- This function transfers control (and one value) to the restart named use-value.
- If no such restart exists, use-value returns nil.
-
- If condition is nil or not supplied, all outstanding restarts are considered.
- If condition is not nil, only restarts associated with that condition are
- considered.
-
- The use-value restart is generally used by handlers trying to recover from
- errors of types such as cell-error, where the handler may wish to supply a
- replacement datum for one-time use.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.4.11. Debugging Utilities
-
- [change_begin]
- Common Lisp does not specify exactly what a debugger is or does, but it does
- provide certain means for indicating intent to transfer control to a
- supervisory or debugging facility.
-
- [Function]
- break &optional format-string &rest format-arguments
-
- [This supersedes the description of break given in section 24.1.-GLS]
-
- The function break prints the message described by the format-string and
- format-arguments and then goes directly into the debugger without allowing any
- possibility of interception by programmed error-handling facilities.
-
- If no format-string is supplied, a suitable default will be generated.
-
- If continued, break returns nil.
-
- Note that break is presumed to be used as a way of inserting temporary
- debugging ``breakpoints'' in a program, not as a way of signaling errors; it is
- expected that continuing from a break will not trigger any unusual recovery
- action. For this reason, break does not take the additional format control
- string that cerror takes as its first argument. This and the lack of any
- possibility of interception by programmed error handling are the only
- program-visible differences between break and cerror. The user interface
- aspects of these functions are permitted to vary more widely; for example, it
- is permissible for a read-eval-print loop to be entered by break rather than by
- the conventional debugger.
-
- break could be defined by
-
- (defun break (&optional (format-string "Break")
- &rest format-arguments)
- (with-simple-restart (continue "Return from BREAK.")
- (invoke-debugger
- (make-condition 'simple-condition
- :format-string format-string
- :format-arguments format-arguments)))
- nil)
-
- [Function]
- invoke-debugger condition
-
- Attempts interactive handling of its argument, which must be a condition.
-
- If the variable *debugger-hook* is not nil, it will be called as a function on
- two arguments: the condition being handled and the value of *debugger-hook*. If
- a hook function returns normally, the standard debugger will be tried.
-
- The standard debugger will never directly return. Return can occur only by a
- special transfer of control, such as the use of a restart.
-
- -------------------------------------------------------------------------------
- Remark: The exact way in which the debugger interacts with users is expected to
- vary considerably from system to system. For example, some systems may use a
- keyboard interface, while others may use a mouse interface. Of those systems
- using keyboard commands, some may use single-character commands and others may
- use parsed line-at-a-time commands. The exact set of commands will vary as
- well. The important properties of a debugger are that it makes information
- about the error accessible and that it makes the set of apparent restarts
- easily accessible.
-
- It is desirable to have a mode where the debugger allows other features, such
- as the ability to inspect data, stacks, etc. However, it may sometimes be
- appropriate to have this kind of information hidden from users. Experience on
- the Lisp Machines has shown that some users who are not programmers develop a
- terrible phobia of debuggers. The reason for this usually may be traced to the
- fact that the debugger is very foreign to them and provides an overwhelming
- amount of information of interest only to programmers. With the advent of
- restarts, there is a clear mechanism for the construction of ``friendly''
- debuggers. Programmers can be taught how to get to the information they need
- for debugging, but it should be possible to construct user interfaces to the
- debugger that are natural, convenient, intelligible, and friendly even to
- non-programmers.
- -------------------------------------------------------------------------------
-
- [Variable]
- *debugger-hook*
-
- This variable should hold either nil or a function of two arguments, a
- condition and the value of *debugger-hook*. This function may either handle the
- condition (transfer control) or return normally (allowing the standard debugger
- to run).
-
- Note that, to minimize recursive errors while debugging, *debugger-hook* is
- bound to nil when calling this function. When evaluating code typed in by the
- user interactively, the hook function may want to bind *debugger-hook* to the
- function that was its second argument so that recursive errors can be handled
- using the same interactive facility.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 29.5. Predefined Condition Types
-
- [change_begin]
- [The proposal for the Common Lisp Condition System introduced a new notation
- for documenting types, treating them in the same syntactic manner as functions
- and variables. This notation is used in this section but is not reflected
- throughout the entire book.-GLS]
-
- X3J13 voted in March 1989 (ZLOS-CONDITIONS) to integrate the Condition System
- and the Object System. All condition types are CLOS classes and all condition
- objects are ordinary CLOS objects.
-
- [Type]
- restart
-
- This is the data type used to represent a restart.
-
-
- ----------------------------------------------------------------
- Table 29-1: Condition Type Hierarchy
-
- condition
- simple-condition
- serious-condition
- error
- simple-error
- arithmetic-error
- division-by-zero
- floating-point-overflow
- floating-point-underflow
- ...
- cell-error
- unbound-variable
- undefined-function
- ...
- control-error
- file-error
- package-error
- program-error
- stream-error
- end-of-file
- ...
- type-error
- simple-type-error
- ...
- ...
- storage-condition
- ...
- warning
- simple-warning
- ...
- ...
-
- ----------------------------------------------------------------
-
- The Common Lisp condition type hierarchy is illustrated in table 29-1.
-
- The types that are not leaves in the hierarchy (that is, condition, warning,
- storage-condition, error, arithmetic-error, control-error, and so on) are
- provided primarily for type inclusion purposes. Normally they would not be
- directly instantiated.
-
- Implementations are permitted to support non-portable synonyms for these types,
- as well as to introduce other types that are above, below, or between the types
- shown in this tree as long as the indicated subtype relationships are not
- violated.
-
- The types simple-condition, serious-condition, and warning are pairwise
- disjoint. The type error is also disjoint from types simple-condition and
- warning.
-
- [Type]
- condition
-
- All types of conditions, whether error or non-error, must inherit from this
- type.
-
- [Type]
- warning
-
- All types of warnings should inherit from this type. This is a subtype of
- condition.
-
- [Type]
- serious-condition
-
- All serious conditions (conditions serious enough to require interactive
- intervention if not handled) should inherit from this type. This is a subtype
- of condition.
-
- This condition type is provided primarily for terminological convenience. In
- fact, signaling a condition that inherits from serious-condition does not force
- entry into the debugger. Rather, it is conventional to use error (or something
- built on error) to signal conditions that are of this type, and to use signal
- to signal conditions that are not of this type.
-
- [Type]
- error
-
- All types of error conditions inherit from this condition. This is a subtype of
- serious-condition.
-
- The default condition type for signal and warn is simple-condition. The default
- condition type for error and cerror is simple-error.
-
- [Type]
- simple-condition
-
- Conditions signaled by signal when given a format string as a first argument
- are of this type. This is a subtype of condition. The initialization keywords
- :format-string and :format-arguments are supported to initialize the slots,
- which can be accessed using simple-condition-format-string and
- simple-condition-format-arguments. If :format-arguments is not supplied to
- make-condition, the format-arguments slot defaults to nil.
-
- [Type]
- simple-warning
-
- Conditions signaled by warn when given a format string as a first argument are
- of this type. This is a subtype of warning. The initialization keywords
- :format-string and :format-arguments are supported to initialize the slots,
- which can be accessed using simple-condition-format-string and
- simple-condition-format-arguments. If :format-arguments is not supplied to
- make-condition, the format-arguments slot defaults to nil.
-
- In implementations supporting multiple inheritance, this type will also be a
- subtype of simple-condition.
-
- [Type]
- simple-error
-
- Conditions signaled by error and cerror when given a format string as a first
- argument are of this type. This is a subtype of error. The initialization
- keywords :format-string and :format-arguments are supported to initialize the
- slots, which can be accessed using simple-condition-format-string and
- simple-condition-format-arguments. If :format-arguments is not supplied to
- make-condition, the format-arguments slot defaults to nil.
-
- In implementations supporting multiple inheritance, this type will also be a
- subtype of simple-condition.
-
- [Function]
- simple-condition-format-string condition
-
- Accesses the format-string slot of a given condition, which must be of type
- simple-condition, simple-warning, simple-error, or simple-type-error.
-
- [Function]
- simple-condition-format-arguments condition
-
- Accesses the format-arguments slot of a given condition, which must be of type
- simple-condition, simple-warning, simple-error, or simple-type-error.
-
- [Type]
- storage-condition
-
- Conditions that relate to storage overflow should inherit from this type. This
- is a subtype of serious-condition.
-
- [Type]
- type-error
-
- Errors in the transfer of data in a program should inherit from this type. This
- is a subtype of error. For example, conditions to be signaled by check-type
- should inherit from this type. The initialization keywords :datum and
- :expected-type are supported to initialize the slots, which can be accessed
- using type-error-datum and type-error-expected-type.
-
- [Function]
- type-error-datum condition
-
- Accesses the datum slot of a given condition, which must be of type type-error.
-
- [Function]
- type-error-expected-type condition
-
- Accesses the expected-type slot of a given condition, which must be of type
- type-error. Users of type-error conditions are expected to fill this slot with
- an object that is a valid Common Lisp type specifier.
-
- [Type]
- simple-type-error
-
- Conditions signaled by facilities similar to check-type may want to use this
- type. The initialization keywords :format-string and :format-arguments are
- supported to initialize the slots, which can be accessed using
- simple-condition-format-string and simple-condition-format-arguments. If
- :format-arguments is not supplied to make-condition, the format-arguments slot
- defaults to nil.
-
- In implementations supporting multiple inheritance, this type will also be a
- subtype of simple-condition.
-
- [Type]
- program-error
-
- Errors relating to incorrect program syntax that are statically detectable
- should inherit from this type (regardless of whether they are in fact
- statically detected). This is a subtype of error. This is not a subtype of
- control-error.
-
- [Type]
- control-error
-
- Errors in the dynamic transfer of control in a program should inherit from this
- type. This is a subtype of error. This is not a subtype of program-error.
-
- The errors that result from giving throw a tag that is not active or from
- giving go or return-from a tag that is no longer dynamically available are
- control errors.
-
- On the other hand, the errors that result from naming a go tag or return-from
- tag that is not lexically apparent are not control errors. They are program
- errors. See program-error.
-
- [Type]
- package-error
-
- Errors that occur during operations on packages should inherit from this type.
- This is a subtype of error. The initialization keyword :package is supported to
- initialize the slot, which can be accessed using package-error-package.
-
- [Function]
- package-error-package condition
-
- Accesses the package (or package name) that was being modified or manipulated
- in a condition of type package-error.
-
- [Type]
- stream-error
-
- Errors that occur during input from, output to, or closing a stream should
- inherit from this type. This is a subtype of error. The initialization keyword
- :stream is supported to initialize the slot, which can be accessed using
- stream-error-stream.
-
- [Function]
- stream-error-stream condition
-
- Accesses the offending stream of a condition of type stream-error.
-
- [Type]
- end-of-file
-
- The error that results when a read operation is done on a stream that has no
- more tokens or characters should inherit from this type. This is a subtype of
- stream-error.
-
- [Type]
- file-error
-
- Errors that occur during an attempt to open a file, or during some low-level
- transaction with a file system, should inherit from this type. This is a
- subtype of error. The initialization keyword :pathname is supported to
- initialize the slot, which can be accessed using file-error-pathname.
-
- [Function]
- file-error-pathname condition
-
- Accesses the offending pathname of a condition of type file-error.
-
- [Type]
- cell-error
-
- Errors that occur while accessing a location should inherit from this type.
- This is a subtype of error. The initialization keyword :name is supported to
- initialize the slot, which can be accessed using cell-error-name.
-
- [Function]
- cell-error-name condition
-
- Accesses the offending cell name of a condition of type cell-error.
-
- [Type]
- unbound-variable
-
- The error that results from trying to access the value of an unbound variable
- should inherit from this type. This is a subtype of cell-error.
-
- [Type]
- undefined-function
-
- The error that results from trying to access the value of an undefined function
- should inherit from this type. This is a subtype of cell-error.
-
- -------------------------------------------------------------------------------
- Remark: [Note: This remark was written well before the vote by X3J13 in June
- 1988 (CLOS) to add the Common Lisp Object System to the forthcoming draft
- standard (see chapter 28) and the vote to integrate the Condition System and
- the Object System. I have retained the remark here for reasons of historical
- interest.-GLS]
-
- Some readers may wonder why undefined-function is not defined to inherit from
- some condition such as control-error. The answer is that any such arrangement
- would require the presence of multiple inheritance-a luxury we do not currently
- have (without resorting to deftype, which we are currently avoiding). When the
- Common Lisp Object System comes into being, we might want to consider issues
- like this. Multiple inheritance makes a lot of things in a condition system
- much more flexible to deal with.
- -------------------------------------------------------------------------------
-
- [Type]
- arithmetic-error
-
- Errors that occur while doing arithmetic type operations should inherit from
- this type. This is a subtype of error. The initialization keywords :operation
- and :operands are supported to initialize the slots, which can be accessed
- using arithmetic-error-operation and arithmetic-error-operands.
-
- [Function]
- arithmetic-error-operation condition
-
- Accesses the offending operation of a condition of type arithmetic-error.
-
- [Function]
- arithmetic-error-operands condition
-
- Accesses a list of the offending operands in a condition of type
- arithmetic-error.
-
- [Type]
- division-by-zero
-
- Errors that occur because of division by zero should inherit from this type.
- This is a subtype of arithmetic-error.
-
- [Type]
- floating-point-overflow
-
- Errors that occur because of floating-point overflow should inherit from this
- type. This is a subtype of arithmetic-error.
-
- [Type]
- floating-point-underflow
-
- Errors that occur because of floating-point underflow should inherit from this
- type. This is a subtype of arithmetic-error.
- [change_end]
-
- -------------------------------------------------------------------------------
-
-
-
-
-
-